Everyone, I am working on an MVVMLight app with WPF but my problem is that I want to fire the Loaded event once the user can load the page. For e.g. I have some navigation pages, so whenever the user clicks on any page, the PageLoaded event should be fired. But in my case it is not fired in the same way. I have a another page that's working perfectly fine. I don't know where I am making a mistake .
My Xaml code looks like this:
xmlns:vm="clr-namespace:Test.User.Facebook.ViewModel"
<UserControl.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator"/>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource Locator}" Path="FriendsList"/>
</UserControl.DataContext>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding LoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
And the ViewModel looks like this :
public RelayCommand LoadedCommand { get; private set; }
public FriendsListViewModel()
{
LoadedCommand = new RelayCommand(() => UserControlLoaded());
}
private void UserControlLoaded()
{
GetFriendsList();
}
This is not loaded when I go to this page. It doesn't fire the event. Someone can help me?
Thanks..
As far as I know the loaded event occurs before the interaction is stared. Therefore, EventToCommand cannot be used to handle the load event. In this case I usually create an event handler, that obtains the command from the DataContext. Then the CanExecute method of the command is evaluated and if it returns true the Execute method is called.
This pattern des not contradict the MVVM pattern and is a clean way out of the occasions when EventToCommand cannot be used. One drawback, however, is hat the CanExecute status is not bound to the enabled property automaticall. But this should not be a problem for the rare ocasions where you have to use this pattern, as when you have no interaction you usually don't have a visual.
Related
I want a solution about close popup control when outside scrollviewer mousewheel changed.
<ScrollViewer>
....
<Grid>
<TextBox x:Name="PART_Text"/>
<Popup IsOpen="{Binding IsDropDown}" StayOpen="False"
PlacementTarget{BInding ElementName=PART_Text">
<Border>...</Border>
</Popup>
</Grid>
</ScrollViewer>
I want to make the pop-up window close Automatically, when a wheel moves, not a mouse click
Tip for the future: you'll have better luck getting questions answered if you make it easy for people. Your code doesn't compile on account of 1) your StaysOpen property is misspelled, 2) your PlacementTarget setting doesn't have an assignment operator and opening quotation, 3) your Binding keyword is mis-capitalized and 4) the setter doesn't have a closing bracket.
To answer your question, all you need to do is add a command handler for the PreviewMouseWheel event. Exactly where you intercept the event depends on what behavior you want; if you want it to occur when any control in your application has focus then add it to the MainWindow, otherwise add it to your ScrollViewer:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<ScrollViewer>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseWheel">
<i:InvokeCommandAction Command="{Binding PreviewMouseWheelCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Then add a command handler for it back in your view model:
private ICommand _PreviewMouseWheelCommand;
public ICommand PreviewMouseWheelCommand => this._PreviewMouseWheelCommand ?? (this._PreviewMouseWheelCommand = new RelayCommand(OnPreviewMouseWheel));
private void OnPreviewMouseWheel()
{
this.IsDropDown = false;
}
So long as your IsDropDown property supports INPC the popup will disappear whenever a PreviewMouseWheel event occurs.
I am new in Caliburn Micro and learn it from this helloworld example. In the example there are only 2 views (.xaml) of type Application and UserControl, and 1 view model.
I avoid to use code behind. Therefore I have only view and view model. I want to know how to catch the window close event of my helloworld application so I can handle it in view model. My target: when user is going to close the app by pressing close [x] button on top-right corner the app gives feedback to the user.
I have read about IViewAware and IScreen, but I find no specific example related to my question.
A simple sample code for view and view model are highly appreciated. Thanks in advance.
PS. I use VS2013, C#.
What you can do is in your View you can attach Caliburn Micro by using
cal:Message.Attach="[Event Closing] = [Action OnClose($eventArgs)]"
So it will look like
<Window cal:Message.Attach="[Event Closing] = [Action OnClose($eventArgs)]">
And on your ViewModel you can just define a public method that says OnClose with CancelEventArgs as the parameter and you can handle it from there.
If your ViewModel inherits Screen, Caliburn Micro has some methods that you can override like
protected override void OnDeactivate(bool close);
this is called when a screen is closed or deactivated or
public override void CanClose(Action<bool> callback)
you can check CanClose usage here
If you are using the BootstrapperBase class you can use:
protected override void OnExit(object sender, EventArgs e)
You're looking for a way to bind an Event to a Command. The typical approach here is to use the EventToCommand behavior from MVVMLight.
Example usage (from the linked article):
<StackPanel Background="Transparent">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<command:EventToCommand
Command="{Binding Main.NavigateToArticleCommand,
Mode=OneWay,
Source={StaticResource Locator}}"
CommandParameter="{Binding Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<!--...-->
</StackPanel>
For your specific scenario, you are not using MVVMLight. Since that framework is open-source, you could copy the implementation of EventToCommand into your own project, or - more simply - you can use the InvokeCommandAction, which is part of the System.Windows.Interactivity.dll library, included with Expression Blend.
Example of InvokeCommandAction:
<TextBox x:Name="TicketNumber">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<i:InvokeCommandAction Command="{Binding OpenTicketCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Lastly, this entire MVVM dogma that you "can't have any code behind" has been shot down time | and | time again (that last link is particularly relevant). MVVM is supposed to be unit-testable, and separates the "View logic" from the "Business logic." The "Close" event is admittedly a bit of a gray area between View and Business logic. But, if you can write an event handler in your code behind, which invokes your ViewModel's appropriate method or command, and if you can unit test that code, then you're as good as gold. Don't worry about removing all traces of code-behind from your project.
One thing I am really not sure about is how to properly pass mouse events to the ViewModel. There is the way of binding triggers using the interactivity extension like for instance in: WPF event binding from View to ViewModel?
But this does not forward the MouseEventArgs to my knowledge, and this solution does not appear very elegant to me.
So what would be the proper solution? One way is to register an event and to handle it in the code behind, e.g.:
private void ListBox_PreviewMouseDown(object sender, System.Windows.Input.MouseEventArgs e)
{
var listbox = sender as ListBox;
if (listbox == null)
return;
var vm = listbox.DataContext as MainViewModel;
if (vm == null)
return;
// access the source of the listbox in viewmodel
double x = e.GetPosition(listbox).X;
double y = e.GetPosition(listbox).Y;
vm.Nodes.Add(new Node(x, y));
}
Here I assume that the listbox's ItemsSource is bound to the vm.Nodes property. So again the question: is it the proper way of doing it? Or is there a better one?
Good timing, I wrote some code to do exactly this about two hours ago. You can indeed pass arguments, and personally I thnk it is elegant because it allows you to fully test your user interface. MVVM Lite allows you to bind events to commands with EventToCommand, so start by adding the relevant namespaces to your control/window:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd ="http://www.galasoft.ch/mvvmlight"
Now add event triggers to the child control whose events you want to intercept:
<ItemsControl ... etc ... >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=MouseDownCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="MouseUp">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=MouseUpCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="MouseMove">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=MouseMoveCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ItemsControl>
In my specific case I'm rendering a collection of items onto a canvas, hence my use of ItemsControl, but it'll work on anything including the parent window. It will also work for key strokes (e.g. KeyDown) but if your child control isn't focus-able then you'll have to add the trigger to the parent instead. In any case all that remains is to add the relevant handlers to your view model:
public class MyViewModel : ViewModelBase
{
public ICommand MouseDownCommand { get; set; }
public ICommand MouseUpCommand { get; set; }
public ICommand MouseMoveCommand { get; set; }
public ICommand KeyDownCommand { get; set; }
// I'm using a dependency injection framework which is why I'm
// doing this here, otherwise you could do it in the constructor
[InjectionMethod]
public void Init()
{
this.MouseDownCommand = new RelayCommand<MouseButtonEventArgs>(args => OnMouseDown(args));
this.MouseUpCommand = new RelayCommand<MouseButtonEventArgs>(args => OnMouseUp(args));
this.MouseMoveCommand = new RelayCommand<MouseEventArgs>(args => OnMouseMove(args));
this.KeyDownCommand = new RelayCommand<KeyEventArgs>(args => OnKeyDown(args));
}
private void OnMouseDown(MouseButtonEventArgs args)
{
// handle mouse press here
}
// OnMouseUp, OnMouseMove and OnKeyDown handlers go here
}
One last thing I will mention that is only a little bit off-topic is that often you'll need to communicate back to the code-behind e.g. when the user presses the left mouse button you might need to capture the mouse, but this can easily be accomplished with attached behaviors. The mouse capture behavior is simple enough, you just add a "MouseCaptured" boolean property to your view model, bind your attached behavior to it and have it's changed handler respond accordingly. For anything more complicated you might want to create an event inside your view model which your attached behaviour can then subscribe to. Either way, your UI is now fully unit-testable and your code-behind has been moved into generic behaviors for re-use in other classes.
I think your approach is good. Those events, that work with View, can be in your code-behind if you handlers work via ViewModel. However, there is an alternative use GalaSoft.MvvmLight (link to download), in which have EventToCommand, supports parameter PassEventArgsToCommand.
Example of using:
<Button>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<cmd:EventToCommand Command="{Binding FooCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
I think you can use both approaches. Your solution is simple, does not require the use of the any frameworks but uses code-behind, in this case it is not critical. One thing is certain, it is advisable not to keep ViewModel event handlers, use the command or store these handlers on View side.
Some new notes
I think, your way does not violate the principles of MVVM, all event handlers working with View, should be on the side of the View, the main thing - it's event handlers need to work with a ViewModel and have a dependency via an interface, but not directly with the UI.
The only principle MVVM that you break - is the mantra "no code" and this is not the main principle of MVVM. The main principles:
Split data Model of View
Application logic should not be tied to UI
Support testability code
Once the code-behind violate at least one of these principles, you must already see the alternatives to solve their problem.
Also, you can read opinions about it on this link:
WPF MVVM Code Behind
Here is my question. I have UserControl that wraps group of buttons and it looks like this: (I show 2 buttons to illustrate what it is)
<Button Content="Cancel"
IsEnabled="{Binding State, Converter={StaticResource CancelEnabledConverter}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction MethodName="Cancel" TargetObject="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="Delete"
IsEnabled="{Binding State, Converter={StaticResource DeleteEnabledConverter}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction MethodName="Delete" TargetObject="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Now, when I place this UserControl on my view - I go by convention and create Cancel and Delete methods on my VM. So, view's XAML looks clean.
I want to create custom control that will have same functionality. Inside control I will have to handle onClick events for buttons and would like to call methods on VM just like it works now. What my code going to look like? I guess I need to access DataContext programmatically and call method by name somehow. I envision using control like so:
<myToolBar Mode="SaveExitDelete" />
So, this will be nice and short. But myToolBar will show 3 buttons and those buttons will call 3 methods(named by convention) on DataContext.
Any pointers?
EDIT
Main question is to how programmaticaly BIND command or method to button. I understand how commanding works, I'm using PRISM and it's got built-in DelegateCommand that I can use. I don't know how to create binding programmaticaly when I know Method name or command name.
Here is how I can see it working:
var button = new DitatToolbarButton();
button.Caption = "Cancel &\nExit";
button.Icon = new BitmapImage(new Uri("img_btn_cancel.png", UriKind.Relative));
button.Command = Binding("CancelCommand");
Obviously 3rd line is wrong but this is what I want. I want to be able to hardcode string that will contain name of command that I will expect VM to have.
Typically, this sort of thing would be done with Commands. In the case of a Button control, which already has the "Command" DependencyProperty, it's as simple as this:
<Button Command="{Binding DoItCommand}">Do it</Button>
and in your view-model class:
private ICommand DoItCommand
{
get
{
return new DelegateCommand(param => DoIt(param), param => CanDoIt(param));
}
}
where DoIt() and CanDoIt() are methods in your view-model and DelegateCommand is defined something like this:
public class DelegateCommand : ICommand
{
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
// ...
There's a decent example of this here. On a custom control, you can declare the Command DependencyProperty yourself. And on a framework control that does not have a Command DependencyProperty, you can use an attached property.
I'm trying to use the search text box (which I made by following this tutorial: http://davidowens.wordpress.com/2009/02/18/wpf-search-text-box/).
I use MVVM & WPF. The above user control works when you write the "Search"-event in the code-behind file of the View, but I can't get it to work with a command (using the ViewModel).
(The search-event fires when you haven't typed something for about 2 seconds.)
I've tried using Caliburn, so it can "map" the view event to the viewmodel method. However when the event fires, the application crashes: "No target found for method SearchText()." on the RaiseSearchEvent method from the custom user control.
See the following test application: Test application
Could somebody tell me what I'm doing wrong? I told CaliBurn to do the following:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Search">
<cal:ActionMessage MethodName="SearchText()" />
</i:EventTrigger>
</i:Interaction.Triggers>
So I figure this is correct. It means that when the "Search" event fires, caliburn will look for the method SearchText in the ViewModel. This doesn't happen though, and it causes my app to crash and burn.
Do you know why? Or how I could solve this problem (doesn't have to be with Caliburn).
I already tried adding "Extending Command Support" (http://msdn.microsoft.com/en-us/library/dd458928.aspx), but this is a little too complex for me :/
Thanks for any help!!
You are using the Caliburn's ActionMessage but because you do not use its Bootstrapper class to start up your application, the MainView's DataContext is not set to an instance of the MainViewModel. If you check the SearchTextBox's DataContext at runtime, you'll see it's null.
Here's a series of steps that may solve your problem (using your linked example project)
Create a class called MyBootstrapper. It should look like this
public class MyBootstrapper : Bootstrapper<MainViewModel> {}
Add your new bootstrapper to the Application's Resources collection, like I show below (App.xaml)
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplicationParadise"
x:Class="WpfApplicationParadise.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<local:MyBootstrapper x:Key="bootstrapper" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Not sure why, but if the bootstrapper isn't nested in my build, it never is instantiated when App.InitializeComponent() is run...
Change App.xaml.cs to simply run InitializeComponent. Note that I had to tweak your build a bit to get this to work... InitializeComponent() is only defined in the App.g.cs file if you have the nested resource dictionary from step 2, or if you have an x:Name attribute on App.xaml, or perhaps other things...
using System.Windows;
namespace WpfApplicationParadise
{
public partial class App : Application
{
public App()
{
InitializeComponent();
}
}
}
Finally, you need to remove the parens as Wallstreet Programmer suggested.
Those steps should cause your App to instantiate your bootstrapper, which in turn instantates the MainViewModel as the root viewmodel of your application, and then create a MainView and hook up its DataContext to the MainViewModel. At that point, your application should work as expected.
Remove ()
<cal:ActionMessage MethodName="SearchText" />
After I run your application, I see that you need to initialize the MainViewModel and also to bind Text of SearchTextBox with TekstBoxTekst.
Codebehind
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
this.DataContext = new MainViewModel();
};
}
}
XAML
<StackPanel>
<l:SearchTextBox
Text="{Binding TekstBoxTekst, UpdateSourceTrigger=PropertyChanged}"
Height="24" x:Name="TekstBoxTekst" Margin="145,144,145,143">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Search">
<cal:ActionMessage MethodName="SearchText">
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</l:SearchTextBox>
</StackPanel>