After looking at some of the MVVM toolkits(MVVM Light, SimpleMVVM) out there the common theme seems to try to use as many blendable events as possible and not use codebehind.
I am not sure how to do all the events. For instance 2 events come to bind when doing windows phone 7.
OnNavigatedTo/From
On Load
Does anyone have examples on how to do this in MVVM? I am using SimpleMVVM but I would hope that the examples might be similar and maybe MVVM light toolkit or even just general MVVM tutorial showing this might help.
I only found ones that show how to do like the button click.
Edit
I am sort of confused on when to use code behind events or use blend events to commands.
For instance in the the MVVM Light tutorials they use MVVM for navigation but why is that better than using a codebehind event?
I am also kinda confused now when people say
Codebehind isn't evil; its the mix of business logic and codebehind
that is problematic. Let your UI handle UI tasks in your codebehind.
Well in the MVVM light examples they have "isbusy" in one of the examples where when the list or whatever it was(forgot) is loading a "loading sign comes up". This was all done in ViewModel and not a codebehind event.
So this seems kinda conflicting to me(maybe I am missing something). What also puzzles me is that if the ViewModel does not know anything about loading how do you know when the loading has started or finished?
As HighCore commented, use EventToCommand. It's pretty simple to use, although you'll need the Blend SDK first.
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.SL4"
xmlns:im="clr-namespace:Microsoft.Expression.Interactivity.Media;assembly=Microsoft.Expression.Interactions"
xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
...
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding GetTweetsCommand}" CommandParameter="Twitter" />
</i:EventTrigger>
</i:Interaction.Triggers>
Also, just a general point on the code-behind: having UI-related functionality in the code-behind isn't the worst thing in the world! It's separate from your ViewModel and it's in a logical place. However, I find behaviours easier to test. For example, using a class inheriting from TargetedTriggerAction allows a storyboard to be controlled by both property changes and controls:
public class ImageAnimationTrigger : TargetedTriggerAction<Storyboard>
{
protected override void Invoke(object parameter)
{
if (Target == null)
return;
if (parameter is DependencyPropertyChangedEventArgs)
{
DependencyPropertyChangedEventArgs args = (DependencyPropertyChangedEventArgs)parameter;
if ((bool)args.NewValue)
Target.Begin();
else
Target.Stop();
}
else if (parameter is RoutedEventArgs)
{
RoutedEventArgs args = (RoutedEventArgs)parameter;
if (!(args.OriginalSource as Button).IsEnabled)
Target.Begin();
else
Target.Stop();
}
}
}
I use this behaviour with a PropertyChangedTrigger as follows:
<i:Interaction.Triggers>
<ic:PropertyChangedTrigger Binding="{Binding Loading}">
<behav:ImageAnimationTrigger TargetName="animStoryboard" />
</ic:PropertyChangedTrigger>
</i:Interaction.Triggers>
As Laurent Bugnion says, use the code-behind if you need to!
1) Unfortunately I didn't find good examples so you are on your own.
2) if you keep your code-behind blank (by using commands instead of button click) you concentrate your code in ViewModel only. I can see two points of benefits:
a) in case you use one ViewModel for different Views (and have some common commands in several vies),
b) in case you use some private variables/methods of your ViewModel which are not available in the View.
3) You may use RaisePropertyChanged to load (reload) data, load data in ViewModel constructor to avoid using OnNavigatedTo or something else.
Related
I want to create resuable components, so naturally I want bindings to pass from parent custom view to child and child of child custom views.
To achieve this I don't use MVVM pattern for custom views, but rather use
BindingContext = this
in my custom views constructors.
Of course I would implement BindableProperty for each property I want but oh boy was I surprised by BindableProperty not working at all!
All because of my "BindingContext = this".
Basically each time I have to write my bindings by hands and not by
<!-- Label in child view -->
<Label x:Name="InnerLabel" Text={Binding LabelText} />
I suspect I'm deeply mistaken somewhere, and there is an easier way to break views into smaller hierarchy of parent-child dependent views, without writing huge amount of boilerplate code like
InnerLabel.SetBinding(Label.TextProperty, new Binding(... etc etc etc
How to write reusable views properly in Xamarin Forms? Why my BindableProperty stopped working with BindingContext = this in my custom views constructors?
Generally speaking, any Forms element will inherit it's parent's BindingContext unless otherwise specified. By assigning BindingContext = this; you are breaking that built in inheritance.
As #Jason properly mentioned in the comment section, the problem with
BindingContext = this
inside custom views constructors.
In chase for easy way wanting to use {Binding CustomViewProp} I was breaking chain of parent -> child context, making BindableProperty properties to receive nothing from parent contexts.
THE PROPER WAY TO USE CUSTOM VIEW PROPERTIES IN XAML, example:
<ContentView x:Name="Self" ... ... ... all other initializing parent element stuff
<Label Text="{Binding CustomViewProp, Source={x:Reference Self}}" />
My page layout looks like this:
<StackLayout x:Name="detailsLayout" VerticalOptions="FillAndExpand">
</StackLayout>
When I create my ViewModel I pass in a pointer to the page. What I would like to do is this:
public class CardsViewModel : BaseViewModel
{
private readonly Cards cardsPage;
public CardsViewModel(Cards cardsPage)
{
this.cardsPage = cardsPage;
}
async public Task CardBtn()
{
cardsPage.detailsLayout.Children.Clear();
But I don't have access to the detailsLayout due to its protection level.
Can someone tell me how I can get access to do this?
The example above is an example of coupling and breaking the separation between the ViewModel and the View(Page). If you want to benefit from MVVM pattern - don't keep a reference to your UI layer and definitely don't try to manipulate the UI layer directly in the ViewModel.
What you should do instead is depends on your needs, however I can give you few ideas:
Encapsulate the logic within the UI control it self, exposing a delegate, command or property you could bind to from your ViewModel to trigger the specific event.
Keep the logic on UI layer, the Page itself and use a MessagingCenter to communicate to it from the ViewModel.
Good luck.
P.S.: You can find more information and examples about MessagingCenter in the official Xamarin documentation. Just don't forget to unsubscribe, otherwise you will find yourself in a bad place.
Scenario
Consider the following XAML inside a view of a Silverlight application:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" MouseLeftButtonDown="Item_Clicked" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The event handler is not called. Apparently, event handlers do not work inside DataTemplate.
There are no warnings or errors, neither at compile-time nor at runtime. Also, the MSDN DataTemplate reference does not specify any restrictions regarding event handlers.
IntelliSense even "helps" me with <New Event Handler> and Navigate to Event Handler. The handler is placed in the code behind of the view.
Questions
Is the behavior specified by Microsoft?
Is it a known bug in VS2010?
Why doesn't the compiler emit an error or
at least a warning?
Remark
I know how to solve the issue since the problem is reported quite often on this site. I want to know why this fails silently.
"Silent error" never gets you very far when your write .NET code, errors are never silent. I'll describe a general way you tackle issues like this, getting you to the "why" automatically and giving you a chance to google a solution.
An important implementation detail you need to know is how XAML is processed when your app is built. It is interpreted by a code generator, it auto-generates C# code which is stored in the obj\Debug directory of your project. After you build, you'll find *.g.cs files there. Take a look at them, you shouldn't have too much trouble making sense of them, the code that was auto-generated from the markup is annotated with #line directives.
Looking at the Silverlight version, MainPage.g.cs, shows little code (edited to fit):
public partial class MainPage : System.Windows.Controls.UserControl {
internal System.Windows.Controls.Grid LayoutRoot;
private bool _contentLoaded;
public void InitializeComponent() {
// etc..
}
}
No sign of the MouseLeftButtonDown event anywhere, this is not encouraging of course.
Do the exact same thing with a sample WPF app. Now take a look at MainWindow.g.cs (again edited to fit and remove the boring parts):
public partial class MainWindow : System.Windows.Window,
System.Windows.Markup.IComponentConnector,
System.Windows.Markup.IStyleConnector {
private bool _contentLoaded;
public void InitializeComponent() {
//...
}
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) {
//...
}
void System.Windows.Markup.IStyleConnector.Connect(int connectionId, object target) {
switch (connectionId)
{
case 1:
#line 9 "..\..\MainWindow.xaml"
((System.Windows.Controls.TextBlock)(target)).MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(this.Item_Clicked);
//...
break;
}
}
Big difference, now you do see the MouseLeftButtonDown event back. Clearly the IStyleConnector interface is instrumental to get that event subscribed.
So go have a look at the MSDN article for that interface. It is rather brief, this is internal plumbing that you shouldn't have to know about, description is:
Provides methods used internally by the WPF XAML parser to attach events and event setters in compiled XAML.
A complete match for what you are trying to accomplish of course. Most important in that article is the Version Information section. Supported in the .NET Framework, but Silverlight is not listed.
This gives you the "why", the necessary plumbing is simply not available in Silverlight. Otherwise not surprising, the most important attribute of Silverlight is that it is small, allowing for a quick download that doesn't slow down the user too much when he browses to a web page that uses it. It can only get small by removing stuff.
So, apparently you are supposed to provide that plumbing yourself. How do you do this? The inspired google query is silverlight datatemplate event. The very first hit is a winner, a question that tries to do exactly what you are doing. Lots more relevant hits available if you need more help.
I'm working on my first C#/WPF project (I'm a Java/Web developer with some Flex/As experience). The MVVM pattern seemed to be the way to go so I've started climbing the learning curve...
I'd like to know what's considered as the way to go to notify state modifications between related ViewModel objects.
Long story short, I have a UserControl containing a TreeView that is bound to a ReadOnlyCollection exposed by MyTreeViewModel.
SomethingViewModel implements INotifyPropertyChanged and generates an event when its 'IsSelected' property is changed.
MyTreeViewModel has an event handler attached to the PropertyChanged event of SomethingViewModel and updates a property that it manages called 'CurrentlySelectedElement'.
MyTreeViewModel also implements INotifyPropertyChanged and generates an event when its 'CurrentlySelectedElement' property changes.
Finally, I have an event handler in another ViewModel class that handles the selection change.
Is this a correct way of approaching this in C#/WPF?
Also, I'm not really fond of using property names with Strings in my event handling methods; It doesn't seem very refactoring friendly to me.. For now, I've dealt with this by exposing the property name as a static string, so that I can simply use the following in my event handler method:
if(SomeViewModel.PROPERTY_IS_SELECTED.Equals(e.PropertyName)) { ... }
Do you know a better alternative? I guess there should be a way of doing this but to be honest I didn't investigate that any further yet.
Thanks for your feedback!
Check out the Event Aggregator pattern. There are quite a few implementations out there. If you're using a MVVM framework ( https://stackoverflow.com/questions/1280462/what-mvvm-framework-are-you-using, What framework for MVVM should I use? ), chances are it will contain an implementation as well.
I have an observableCollection bound to a pivot control in my UI. When I try to update the collection (clear() items and recreate) everything works fine unless the selectedIndex of the pivot control is bigger or equal to 2.
In that case I get an ArgumentOutOfRange exception when I try to call Add() to the observable collection. It is very strange.
I tried creating a new observable collection and then Add() items there, this seems to work but I doesn't refresh the UI unless I call my update function twice.
What can be wrong? Is this a bug?
This is a known issue.
Unhandled Exception When Setting Pivot Control SelectedItem/SelectedIndex Property to 3rd Pivot Item (WP7)
Not deferring navigation/(binding?) to the loaded event is a workaround.
You have probably alreade solved it but here is what I did.
As mentioned before this is a known "bug"/limitation.
You can however set the SelectedIndex in the Loaded event for the page.
See here:
http://christian-helle.blogspot.com/2011/02/working-around-pivot-selectedindex.html
This helped me and it works just fine now =)
To try and save load and performacne overhead, the framework only loads the currently displayed pivot and the ones either side. The other items are delay loaded when the neighbouring item is displayed. As a consequence of this you can experience issues when trying to set the SelectedItem to an item which hasn't been loaded or the page hasn't finished loading fully.
If you can share some code to demonstrate what you're trying to do we may be able to provide some more specific help.
As mentioned. The Pivot control is optimised to not load all the panels. If you are trying what I think you're trying, then I would suggest you switch to a Panorama Control which initalises all the PanoramaItems.
I'm going to try the fix suggested #Jimmy Engtröm. However, I've also been able to work around this by waiting until the Load has occurred.
<controls:Pivot x:Name="pivotCtrl" Title="{Binding ApplicationTitle}"
Loaded="OnPivotControlLoaded" Opacity="1">
And in the page's code behind:
private void OnPivotControlLoaded(object sender, RoutedEventArgs e)
{
// Restore the Pivot control's SelectedIndex
if (State.ContainsKey(SelectedPivotIndexKey))
{
pivotCtrl.SelectedIndex = State.Get<int>(SelectedPivotIndexKey);
}
myStoryboard.Begin();
}
Now, why the Storyboard? Well, when you wait until Load, you will see the first pivot and that's lame. So the Storyboard does a quick fade-in...just enough to disguise the fix. I tried just setting Visibility, but that would crash the app. Also note that, for design purposes, I leave Opacity set to 1 in the pivot control's XAML. Here's the Storyboard:
<Storyboard x:Name="myStoryboard">
<DoubleAnimation
Storyboard.TargetName="pivotCtrl"
Storyboard.TargetProperty="Opacity"
From="0.0" To="1.0" Duration="0:0:01"
/>
</Storyboard>
Here are the helper functions (placed in a separate class file and referenced, e.g. using MyApp.Helpers and the class file needs to reference System.Collections.Generic)
public static T Get<T>(this IDictionary<string, object> dictionary, string key)
{
return (T)dictionary[key];
}
public static void AddOrReplace<T>(this IDictionary<string, T> dictionary, string key, T item)
{
if (dictionary.ContainsKey(key))
dictionary.Remove(key);
dictionary.Add(key, item);
}
Again, it's not the greatest fix but it works alright and the fade-in is actually something I might employ elsewhere.