basically I have a pivot control in my WP7 app that contains 3 views. On each view I'm calling 1 of my 3 different web services that I run. What I'm trying to do is call the service only when they navigate to that particular view.
It's pretty simple using the code behind because all you do is use selected index with a switch statement and you can fire certain methods accordingly. Any idea on how to accomplish this from a view model?
NOTE: I'm using MVVM Light.
UPDATE: Here's my code that I would normally use:
private void PivotItem_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int currentPivot = ResultsPivot.SelectedIndex;
switch (currentPivot)
{
case 0:
//Fire Method 1
break;
case 1:
//Fire Method 2
break;
case 2:
//Fire Method 3
break;
default:
//Fire default method
break;
}
}
The standard approach with MVVMLight is the split your view-model into data and commands. Most things you use databinding related, properties, etc. but commands actually do something.
In this case what you are calling "Fire Method 1" is an ordinary method that to conform to the pattern you have to convert to a command. If you already have commands you know what I am talking about.
The glue for events like SelectionChanged that you would have wired up with code-behind in MVVMLight is EventToCommand which is a XAML fragment that you put in the XAML with the pivot item instead of in the event hander.
So this is the pattern: EventToCommand is your key to hooking up XAML events to view-model commands without any code-behind. The best thing to do is use the MVVMLight samples to see how EventToCommand works because there are lots of ways to use it.
But here is the bare-bones version:
<controls:PivotItem Name="pivotItem">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding SelectServiceCommand}"
CommandParameter="{Binding SelectedIndex, ElementName=pivotItem}"/>
</i:EventTrigger>
<!-- other stuff -->
</i:Interaction.Triggers>
</controls:PivotItem>
and to make this work the SelectServiceCommand has to actually exist in the view-model and it has to take a parameter and do the right thing for 0, 1, 2, 3, etc.
This can be solved in the following way
<controls:Pivot x:Name="PivotControl" FontSize="18" >
<Custom:Interaction.Triggers>
<Custom:EventTrigger EventName="SelectionChanged">
<GalaSoft_MvvmLight_Command:EventToCommand x:Name="VideoPivotClicked"
Command="{Binding VideoPivotClicked, Mode=OneWay}" PassEventArgsToCommand="True" />
</Custom:EventTrigger>
</Custom:Interaction.Triggers>
Then in your viewmodel you add this
public RelayCommand<SelectionChangedEventArgs> VideoPivotClicked
{
get;
private set;
}
VideoPivotClicked = new RelayCommand<SelectionChangedEventArgs>(arg =>
{
PivotItem pivotItem = arg.AddedItems[0] as PivotItem;
Pivot pivot = pivotItem.Parent as Pivot;
Debug.WriteLine(pivot.SelectedIndex);
}
);
You will not get the PivotItem that you are going to! and not the one you are leaving.
I haven't used MVVM Light directly, but you should be able to bind the selected index / item to a property on the view model. When that property is changed you could do your switch.
I like to keep things simple in situations like these where the View needs to notify the ViewModel that something that is so trivial changed (for example: A trivial combobox selection change that really has nothing to do with the view state (i.e ViewModel)).
For your specific case, in your switch statement, just call a public method in your ViewModel.
How to get the viewmodel reference? You can obtain that by the view's DataContext. So now your views can call public methods (and properties) within your viewModel.
For significant things stick with DataBinding. otherwise, just call directly. Saves so much time and hassle.
I get the index for the pivotItem that I'm leaving, not the PivotItem that I'm going to! .
Using this:
<controls:Pivot x:Name="pivMain" Title="{Binding AppName}" >
<Custom:Interaction.Triggers>
<Custom:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding SelectServiceCommand}"
CommandParameter="{Binding ElementName=pivMain, Path=SelectedIndex}"/>
</Custom:EventTrigger>
</Custom:Interaction.Triggers>
Related
I have a list view that is populated from a mysql table, I want to be able to double click on a item in the listview and bring up a new window with more information. How do I pass the first column value to the new window (this is the id of the item)? This way I can make another query to get the rest of the info about the item.
Was having a similar issue with a ListBox wanting to open a window (Different View) with the SelectedItem as the context (in my case, so I can edit it).
The three options I've found are:
1. Code Behind
2. Using Attached Behaviors
3. Using Blend's i:Interaction and EventToCommand using MVVM-Light.
I went with the 3rd option, and it looks something along these lines:
<ListBox x:Name="You_Need_This_Name"
ItemsSource="{Binding Your_Collection_Name_Here}"
SelectedItem="{Binding Your_Property_Name_Here, UpdateSourceTrigger=PropertyChanged}"
... rest of your needed stuff here ...
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<Command:EventToCommand Command="{Binding Your_Command_Name_Here}"
CommandParameter="{Binding ElementName=You_Need_This_Name,Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
That's about it ... when you double click on the item you want, your method on the ViewModel will be called with the SelectedItem as parameter, and you can do whatever you want there :)
What is meant with MVVM is that you will have for example a ViewModel containing a property SelectedThing bound to the SelectedItem of the listview and a command that gets executed using EventCommand on the MouseDoubleClick event of the View which will execute in the end the operation you want on the SelectedThing which could be also passed in as parameter to the command also by binding.
I'm developing an application in which the user needs to enter a username and password to login.
I'm using the MVVM light framework to separate my view from my viewmodels.
For now I have a button on my view which tapped event is caught on the corresponding viewmodel.
I would also would like to have the possibility if the user presses the enter button they can also login. In WPF you had the ability to set a button as IsDefault, but it seems not possible in WinRT.
How can I do this?
I think you can put this code into your viewmodel and check if the enter key is pressed, then when it is pressed just launch the same method you would launch if your button was pressed
Windows.UI.Xaml.Window.Current.CoreWindow.KeyDown += (sender, arg) =>
{
if (arg.VirtualKey == Windows.System.VirtualKey.Enter)
{
//Your login method
}
};
As per my comment I don't recommend using UI code in the view model. Use a trigger to bind to a command for a keypress in the textbox.
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cmd:EventToCommandCommand="{Binding MyCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
be sure to incluide the following in your xaml declarations:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight"
Hope this helps
There's a virtual method on the Page called OnKeyDown() you can override. This is safer than subscribing to events and fits MVVM better. I prefer to cast DataContext to my view model type and call whatever I want.
It is possible to achieve this with behaviors:
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="KeyDown">
<core:CallMethodAction TargetObject="{Binding}" MethodName="Load" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
You'll have to reference:
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
However, it only works for all keys. I didn't see any possibility to filter for a certain key.
There are:
xmlns:i="using:WinRtBehaviors"
xmlns:b="using:Win8nl.Behaviors"
<i:Interaction.Behaviors>
<b:EventToCommandBehavior Event="Loaded" Command="LoadedCommand"/>
</i:Interaction.Behaviors>
from Nuget.
I'm binding my viewmodel and view using resource dictionary as follows
<DataTemplate DataType="{x:Type viewmodels:MyViewModel}">
<Views:MyView />
</DataTemplate>
in MyView, i have dataGrid x:Name="BoxDataGrid" with DataGrid.RowDetailsTemplate having other dataGrid x:Name="SpoolsDataGrid"
how to access MyView or datagrids above using code behind in MyViewModel ?
The reason is,i want to load and show contents inside RowDetailsTemplate only when main datagrid row selected (clicked) thru event "RowDetailsVisibilityChanged".
Thanks.
Correction:
My bad. I want to access MyView not MyViewModel
It's quite easy. DataContext property in your MyView object points to concrete object of MyViewModel. So you can use XAML bindings to this view-model or access in code-behind e.g.
MyViewModel model = (MyViewModel) DataContext;
asktomsk's answer is correct. You can access the ViewModel via the DataContext property.
However, with a little bit more effort you can almost always get around directly accessing the ViewModel from the view. The whole point of MVVM or MVC is that there aren't dependencies from View to ViewModel.
Things you should research in WPF for MVVM include:
Attached Properties
Attached Behaviors
Mediators
Value Converters
Markup Extensions
You need to be aware of all of these to find elegant solutions to some problems you encounter with MVVM.
You'll need to specify a bit more detail about the behaviour you are trying to get if you want us to help you figure out how you can do it without accessing the ViewModel through the datacontext.
You can, for instance, bind somethings' Visibility to a boolean in the ViewModel using a converter?
I apologize if you know all of the above already.
Just solved this problem using MVVM Light Toolkit - EventToCommand. Other better suggestions are very much welcome.
http://blog.galasoft.ch/archive/2009/11/05/mvvm-light-toolkit-v3-alpha-2-eventtocommand-behavior.aspx
Hope this solution will be useful to others.
don't need to know the view, in my datagrid view
<i:Interaction.Triggers>
<i:EventTrigger EventName="RowDetailsVisibilityChanged">
<cmd:EventToCommand Command="{Binding RowDetailsVisibilityChangedCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
and in viewmodel
public RelayCommand<DataGridRowDetailsEventArgs> RowDetailsVisibilityChangedCommand
{
get;
private set;
}
and in viewmodel constructor
RowDetailsVisibilityChangedCommand = new RelayCommand<DataGridRowDetailsEventArgs>(e =>
{
DataGrid SpoolsDataGrid = e.DetailsElement as DataGrid;
DataRowView drv = (DataRowView)e.Row.Item;
serialNo = drv.Row["BOX_SERIAL"].ToString();
SpoolsDataGrid.ItemsSource = as400Service.GetSPOOL_BY_SERIAL_NO(serialNo);
});
I have seen somewhere this is 100% doable using blend behaviors, cannot find example.
Even better example would be to pass event args and/or sender as CommandParameter to specific command.
<i:Interaction.Triggers>
<i:EventTrigger EventName="SizeChanged">
<ei:CallMethodAction MethodName="WndSizeChanged"
TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
WndSizeChanged method should be public and have same signature as event delegte it subscribing to.
a comfortable way to create behaviors is to use the DelegateCommand approach as in Prism:
Read here: Prism behavior
Caliburn Micro has some nice ways to do this.
From the docs:
<Button Content="Remove"
cal:Message.Attach="Remove($dataContext)" />
$eventArgs – Passes the Trigger’s EventArgs or input parameter to your Action. Note: This will be null for guard methods since the trigger hasn’t actually occurred.
$dataContext – Passes the DataContext of the element that the ActionMessage is attached to. This is very useful in Master/Detail scenarios where the ActionMessage may bubble to a parent VM but needs to carry with it the child instance to be acted upon.
$source – The actual FrameworkElement that triggered the ActionMessage to be sent.
$view - The view (usually a UserControl or Window) that is bound to the ViewModel.
$executionContext - The actions's execution context, which contains all the above information and more. This is useful in advanced scenarios.
$this - The actual ui element to which the action is attached.
Is it possible to create a command behavior using Prism's CommandBehaviorBase class for Silverlight's grid? I know that it is only intended for actual controls, so I was wondering if anyone might know if a workaround. I would like to create an attachable mouse over behavior for a grid, that executes a specific command, and ideally would like to use Prism for this approach, just can't seem to use CommandBehaviorBase for a Grid.
Thanks.
The arguably easier way to achieve this is to use Triggers. Doesn't require you to write any code, all you have to do is this:
<Grid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<si:InvokeDataCommand Command="{Binding DoSomethingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
...
</Grid>
Here the DoSomethingCommand (defined in a ViewModel) will trigger when MouseEnter event is fired on the Grid.