Handling different view model states in MVVM - c#

I’m using the MVVM pattern for my metro app. On my main page, the user presses a button and the app gets the current location of the user.
The process of getting the user’s location is asynchronous and I want the UI to change, so the button will be disabled and an indeterminate progress bar will display until the co-ordinates are returned.
What is the best way to manage this according to MVVM? Having a custom visual state? I notice that there are ApplicationViewStates and CommonStates, is it possible to add your own custom ones?
How would you do this?

I think you're looking for something like:
viewModel:
private async void CommandExecution()
{
IsAwaitingResponse=true;
var response = await _myService.DoNetworkCall()
IsAwaitingResponse=false;
}
If you're using one of the microsoft templates you should have a BooleanToVisibilityConverter sitting in your "Common" folder. You use it like so:
<Page.Resources>
<BooleanToVisibilityConverter x:key="booleanToVisibilityConverter"/>
</Page.Resources>
"..."
<ProgressBar Visibility={Binding IsAwaitingResponse,Converter={StaticResource booleanToVisibilityConverter}}" IsIndeterminate="true"/>

My xaml experience is with WPF and Silverlight, but I hope this suggestion works for a Metro app. You can bind the button's IsEnabled property and the progress bar's visibility property to a boolean property (maybe call it IsCurrentLocationComplete) on your view-model. You'll change this property's value when the async method is complete and your view will be updated. Setting the Button's property is straight forward, but you'll need a value converter (IValueConverter) for the progress bar's visibility one.

Related

WPF Binding on startup

In my WPF project, I have the Visibility of a Grid bound to a bool with a BooleanToVisibilityConverter:
<Grid ... Visibility="{Binding SelectedElement.HasError, Converter={StaticResource ResourceKey=BoolToVis}}">
However on startup the grid is always shown for a few milliseconds, even though the element/class holding the bool field is not even created, and after creation is defaulted to false.
How can I prevent the grid from showing up on startup? I tried to implement a FallbackValue for the binding, because the object in the path is not yet available, but setting "Visible" or "Hidden" here doesn't change anything.
(Posted on behalf of the OP).
Works fine with FallbackValue=Collapsed.

Getting a value from a child WPF window that has a seperate viewmodel using the MVVM pattern

I have made quite a bit of progress on my first MVVM WPF application, the issue I am now having is I have a Window that has a viewmodel. This window has a button which opens another window that has another viewmodel.
Imagine a textbox on the first window. Once the second is opened the user will select a value and click save, this window will close and update the first window with its value.
When pushing save I have an ICommand on the childwindows Viewmodel that calls the SaveMethod. I have the selected value stored in a property on the Child windows viewmodel. But how do I update the Main Windows textbox with this value? I imagine I bind a property on the main windows view model, but unsure on how to continue.
Please advise, I can provide code examples if needed, but I think I may have explained it well enough, oh and thanks to everyone at StackOverflow for the help on my questions I have learnt a lot.
This is pretty straightforward using the MVVM Light framework. For the purposes of demonstration I'm going to use a string as the value you're passing, but it's easy to construct a different message type for whatever you need to pass.
In the constructor of your first Window's ViewModel you register to receive NotificationMessages. NotificationMessages are used to send string messages:
public MyFirstViewModel()
{
Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
}
In the SaveMethod in your second Window's ViewModel you send a message with the value you want to pass. I'm using MyStringValue as the name of the property that stores the value chosen by the user in your second Window:
private void SaveMethod()
{
MessengerInstance.Send(new NotificationMessage(MyStringValue));
}
When that message is received by the ViewModel of the first Window the NoitificationMessageReceived method is called. I'm going to put that value in a string property on the first ViewModel called MySavedValue:
private void NotificationMessageReceived(NotificationMessage msg)
{
MySavedValue = msg.Notification;
}
In your View for the first Window you have a TextBox with its Text property bound to MySavedValue. This updates whenever MySavedValue is updated.
In the parent viewmodel, you'll need a reference to the child viewmodel. When the child window is closed, you'll want to get the value of the secondviewmodel's property and set it to a appropriate property of the first parent viewmodel.
One of the posible (and simple) solutions is to keep one ViewModel for both windows
<Grid>
<StackPanel>
<TextBox Text="{Binding TheText}" />
<Button Command="{Binding ShowOptionsCommand}" Content="..."/>
</StackPanel>
<Popup IsOpen="{Binding IsShowingOptions}">
<StackPanel>
<ListBox ItemsSource="{Binding Options}"
SelectedItem="{Binding SelectedOption,Mode=TwoWay}"/>
<Button Command="{Binding SaveOption}">Save</Button>
</StackPanel>
</Popup>
</Grid>
//ShowOptionsCommand handler
void ShowOptions()
{
IsShowingOptions = true;
}
//SaveOptionCommand handler
void SaveOption()
{
TheText = SelectedOption;
IsShowingOptions = false;
}
I'm using the Popup to simplify the example.
Personally I'd go with the mvvm light framework already mentioned, but another option is to leverage IOC, also included with the above framework.
With this pattern view models have interfaces and are bound as properties from a view model locator data source. Within that, the child view model can be injected to the parent view model. Because IOC can create singleton instances of objects, the same instance gets given to the parent as is bound to the child window. That way you get a reference to the view model but through an interface thus preserving the separation.
Just offering this as an alternative technical solution beyond those already offered.

Use SendKeys in a UserControl

I want to use the C# System.Windows.Forms.SendKeys.SendWait() Method to send Keystrokes from an OnScreenKeyboard to a Textbox. Since I may use this OnScreenKeyboard at other places too I created a UserControl with View (for the Design of the Keyboard) and Viewmodel (basically for calling the Sendkeys.SendWait() Method) in an extra project.
Within this project I created a MainView where I included the UserControl via a ContentControl as you can see in the Code below. CurrentPage basically refers to the Viewmodel of the Keyboard.
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:KeyboardViewmodel}">
<view:KeyboardView/>
</DataTemplate>
</Window.Resources>
<Grid>
<Border Background="White">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"/>
</Border>
<TextBox Width="120"/>
</Grid>
I now have the OnScreenKeyboard and a Textbox in my Window. Clicking into the Textbox and pressing buttons of my OnScreenKeyboard will result in text appearing in my Textbox. All Controls within the KeyboardView are set to avoid getting focus. This is necessary to maintain focus on the Textbox.
The Buttons in the KeyboardView all bind to the Command ClickCommandin my KeyboardViewmodel. Here is the code of the KeyboardViewmodel:
public class KeyboardViewmodel : BaseModel
{
public BaseCommand ClickCommand { get; set; }
public KeyboardViewmodel()
{
ClickCommand = new BaseCommand(PressAndRelease);
}
public void PressAndRelease(object key)
{
if (((string)key).Length <= 1)
SendKeys.SendWait((string)key);
else
SendKeys.SendWait("{" + (string)key + "}");
}
}
Now I did create a NuGet Package with these Files and imported them to the project where I want to use my OnScreenKeyboard.
I did do basically the same as when I tested the OnScreenKeyboard before.
But let me explain the structure of the project a little more:
I have a MainView + MainViewmodel. The MainViewmodel manages the navigation between available pages. To show these pages I have - as in the short example before - a ContentControl whose content is bound to a CurrentPage Property. The MainViewis a normal Window, all other Views are UserControls.
In one of these pages I need an OnScreenKeyboard (DetailsView + DetailsViewmodel). So it seemed logical to me to use another ContentControl within the DetailsView:
<Border Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="3" Height="Auto" Width="Auto">
<ContentControl Content="{Binding Path=OnScreenKeyboard}"/>
</Border>
I create the KeyboardViewmodel in the constructor of the DetailsViewmodel. The constructor of the DetailsViewmodel is called in the MainViewmodel at startup.
So now everything works out fine so far, the OnScreenKeyboard is shown on the correct page in the correct place. If I click a button of the OnScreenKeyboard the proper bound command is called and the SendKeys.SendWait() Method is called.
But no text appears in the TextBox. I have a very bad understanding of the SendKeys.SendAwait() Method. Also, the MSDN Documentation seems to be not very exhaustive on this topic.
It states: "Sends the given keys to the active application, and then waits for the messages to be processed."
Now. The Active / Focused Application is my Application. So my guess is that the KeyStrokes should be processed by my Textbox.
My Questions:
Any guesses how to debug the 'SenWait()' Method further e.g. track where the strokes are really sent to or something like that?
Is this the correct way for sending KeyStrokes to an active Application? It seems like SendKeys comes from Windows Forms, I use WPF.
Should I just pass my Textbox as reference to the OnScreenKeyboard and write directly to the referenced Textbox? This would make me much less flexible in regards of reusability.
Update:
As pointed out in the comments this could probably be a duplicate question.
I am well aware of the various different solutions and have already considerd them:
http://wpfkb.codeplex.com/
http://www.codeproject.com/Articles/32568/A-Touch-Screen-Keyboard-Control-in-WPF
http://www.codeproject.com/Articles/145579/A-Software-Virtual-Keyboard-for-Your-WPF-Apps
But as one may understand these projects are looking all way too powerfull for my simple needs.
Here a screenshot to provide a better understanding of my needs:
It is really as simple as that. 4 rows of buttons that will never change, no other controls / functionality than sending the CommandParameter of the pressed button to the Textbox / Active Form.
Researching on that specific problem hasn't shown any problems like that. In most other SO Questions the problem is to send Data to another Window, not to send Data WITHIN the current Window.
So I don't consider this question as duplicate.

MVVM Light & WPF - Binding Multiple instances of a Window to a ViewModel

I am working on my first project in MVVM and I've chosen to use the MVVM Light Toolkit. I have a GameViewModel that handles business on the main screen of my game. I need to find out how to open a new window (AdventurerView) with an instance of Adventurer as a parameter when a command is executed, have it bound to AdventurerViewModel, and display and return data. Instances of this window will be opened and closed frequently. I have been stuck on this for a couple of days now and it's driving me crazy. I would like to learn how to do this in an MVVM-friendly way, preferably with the tools provided by MVVM Light or pure XAML.
I've tried using MVVM Light's ViewModelLocator but since AdventurerView is a window it won't work; it says "Can't put a Window in a Style", though the program still compiles and runs. Could there be something I could change to make that work? Or is there another way to bind them in XAML? Or another approach entirely? I would really love to be able to move on from this. I have also tried using MVVM Light's messenger to no avail (which still doesn't tackle the View/ViewModel issue).
I just need to be able to create a window that is bound to AdventurerViewModel and display/return the appropriate data.
AdventurerView.xaml is in its default state at the moment, but I feel that if I could bind the appropriate data that might help (DataContext).
AdventurerViewModel is pretty bare-bones as well
class AdventurerViewModel : ViewModelBase
{
#region Members
private Adventurer _adv;
#endregion
#region Properties
public Adventurer Adv
{
get { return _adv; }
set { _adv = value; }
}
#endregion
#region Construction
public AdventurerViewModel(Adventurer adv)
{
this._adv = adv;
}
#endregion
}
App.xaml with the non-working DataTemplate at the bottom:
<Application StartupUri="MainWindow.xaml"
xmlns:views="clr-namespace:AoW.Views"
xmlns:vm="clr-namespace:AoW.ViewModels"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AoW.App"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
<DataTemplate DataType="{x:Type vm:GameViewModel}">
<views:GameView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:TitleViewModel}">
<views:TitleView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:AdventurerViewModel}">
<views:AdventurerView />
</DataTemplate>
</Application.Resources>
</Application>
The command in GameViewModel that will hopefully make this all happen (the messagebox just confirms that the command is firing):
private void ExecuteShowAdvCommand(Adventurer adv)
{
System.Windows.MessageBox.Show(adv.Name);
}
I don't really know what else to include.
Ok I put together a demo that should make this hopefully easier for you Download Link
Functionality:
3 Windows in Total (MainWindow, ModalWindow, NonModalWindow)
MainWindow has a TextBox you can type whatever you want into.
2 buttons on the top will open the Modal / NonModal Window accordingly
Each window when opened will display the message that was in MainWindow's TextBox in a TextBlock inside them.
In each window you can tick a CheckBox to update the value in result's textblock in MainWindow (For the Modal Window this will kick in when modal window is closed. For NonModal changes can be seen asap)
That's it for functionality,
Concepts:
Registering Multiple VM's with the SimpleIoC and using GetInstance(...) to request them out.
Messenger class usage with a custom message type OpenWindowMessage
Opening Modal / Non Modal Windows from a parent VM staying true to the MVVM principles
Passing data between windows(just shown in NonModal)
Important Note:
- The method used in this example to set the non DP DialogResult from the modal window is not MVVM friendly cos it uses code-behind to set the DialogResult property on a Window.Closing event which should be avoided(If needing to be "testable"). My preferred approach is a bit long and is very well documented HERE(Mixture of question and answer). Hence why I ignored it for the sake of this sample.
Follow up to Viv, I modified the sample to include an example of opening the window without using a code behind.
Sample project is here.
I'm utilizing the ViewModelLocator singleton with a static method that news up the viewmodel and window and Data Context instead of the code behind.
Blog Post with Details.
Let me know which method is preferable. I dislike using code behind, but there could be pro's and con's I'm missing.

MVVM Light - Multiple ViewModels (and connecting them up)

I am trying to learn the MVVM pattern (C#), having come from a Windows Forms background. I am using the MVVM Light toolkit, and so far I think it is brilliant.
I have made several small applications, however one thing I am struggling with is introducing a second view.
I want to (for example), have a button on my MainViewModel, which via a RelayCommand, opens up a new Window - let's say an "About" window. I have done hours of research on the web for this however it seems I can't get my AboutViewModel to communicate with/show my AboutView.
I have placed a receiving messenger in the code-behind constructor of the AboutView.xaml - however I can't get it to receive any messages from the AboutViewModel, and thus can't make it 'Show()'.
If anyone has an example of an Mvvm Light WPF app using multiple views that would be great :)
There are two ways I can think to do this easily
The first would be to use a Popup instead of a new Window. For example, I often put properties in my ViewModel for PopupContent and IsPopupVisible, and set those values anytime I want to display my Popup control. For example, a ShowAboutPopup relay command might run something like this:
void ShowAboutPopup()
{
PopupContent = new AboutViewModel();
IsPopupVisible = true;
}
You can display it using a Popup object, or a custom UserControl. I prefer to use my own custom Popup UserControl, which will usually end up looking like this:
<Window>
<Canvas x:Name="RootPanel">
<SomePanel>
<!-- Regular content goes here -->
</SomePanel>
<local:PopupPanel Content="{Binding PopupContent}"
local:PopupPanel.IsPopupVisible="{Binding IsPopupVisible}"
local:PopupPanel.PopupParent="{Binding ElementName=RootPanel}" />
</Canvas>
</Window>
The PopupContent property is a ViewModel (such as an AboutViewModel), and DataTemplates are used to tell WPF to draw specific ViewModels with specific Views
<Window.Resources>
<DataTemplate DataType="{x:Type local:AboutViewModel}">
<local:AboutView />
</DataTemplate>
</Window.Resources>
The other method is to have some kind of ApplicationViewModel that runs on startup, and is responsible for the overall application state, which includes which window(s) are open.
Typically I prefer to have a single ApplicationView that contains a ContentControl to display the current page
<Window>
<ContentControl Content="{Binding CurrentViewModel}" />
</Window>
however it can also be used to manage multiple windows. If you do use it to manage multiple Window objects, be warned that this will not be a pure ViewModel because it will need to access some View-specific objects, and referencing UI objects it not something a ViewModel should do. For example, it may subscribe to receive ShowWindow messages, and upon receiving those messages it would create the specified View and show it, and possibly hide the current window as well.
Personally, I try to avoid multiple windows as much as possible. My usual method is to have a single View that contains consistent application objects for any page, and a ContentControl containing dynamic content that changes. I have an example using this navigation style on my blog if you're interested
As i can see you want a navigation in your MVVM app?
Word goes to the creator of MVVM Light - Laurent Bugnion - with his post about using Navigation Service for switching Views. It's actually about Windows Phone & Silverlight but same should apply to WPF.
Also this answer in related question uses this approach.

Categories