Using a command on a custom control - c#

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>

Related

Why TriggerEvent "Loaded" binded command doesn't work for App.xaml

I'm trying to load a Splash Screen when my app loads.
The LoadSplashScreen is a Delegate Command that checks if there is already another program running, if not it shows the splash screen.
But to check the property, I need to trigger a Command when the App.xaml is loaded.
It shows me that "Triggers can't be attached to elements of type Application"
And other errors also which I think are related to the first one where it says that Triggers binding can only be derived types of "DependencyObject".
Here's the xaml code:
<Application x:Class="FST.CWI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:FST.CWI.Sources.ViewModel"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
StartupUri="MainWindow.xaml"
xmlns:interactivity="http://schemas.microsoft.com/expression/2010/interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadSplashScreen}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Application.Resources>
<viewModel:BaseViewModel x:Key="BaseVM" />
<viewModel:AppViewModel x:Key="AppVM" />
<viewModel:GeneralViewModel x:Key="GeneralVM" />
<viewModel:SteeringViewModel x:Key="SteeringVM" />
<viewModel:AdvancedViewModel x:Key="AdvancedVM" />
<viewModel:SittingViewModel x:Key="SittingVM" />
</Application.Resources>
</Application>
Wpf has a spash screen feature, maybe you can use it:
https://learn.microsoft.com/en-us/dotnet/framework/wpf/app-development/how-to-add-a-splash-screen-to-a-wpf-application
It can display an image until your MainWIndow appears.
Application is not a dependency object so don't try and use anything which relies on it being one. Because that just isn't going to work.
Remove the startup url from app.xaml that opens mainwindow.
Instead point it to an override for onstartup in you app.xaml.cs
<Application …
Startup="OnStartup">
And of course your code in app.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Your logic here
}
Put whatever your logic in there and new up, show whichever window or whatever you like.
Which will look something very roughly like:
SplashScreen ss = null;
if ( your criteria goes here)
{
ss = new SplashScreen("whatever.bmp");
ss.Show();
}
MainWindow mw = new MainWindow();
if ( your criteria goes here )
{
ss.Close();
}
mw.Show();

Attach to a click event in XAML control when loading XAML dynamically.

I have a XAML control which gets loaded dynamically at runtime. This is pure XAML with no code behind.
I dont have any control over the parent loading mechanism which is why this looks a bit weird.
I have a parent application which loads my Plugin DLL and loads my XAML Control.
My DLL signature is:
public class Application : BaseClassHere
{
public Application(IParentContext context) : base(context)
{
// Im placing this instance in the bag which i use in my XAML
base.MyObservablePropertyBag["MyParentContext"] = new ObservableValue<object>(this);
}
}
My XAML is like this:
<av:UserControl
xmlns:local="clr-namespace:MyApplicationNS;assembly=MyDll"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" >
<Border DataContext="{Binding MyObservablePropertyBag[MyParentContext].Value}" >
<Button Name="MyButton" />
</Border>
</UserControl>
This binding works perfectly in the XAML. I have full access to all public properties that i definein the Application class.
My problem is that i want to link to up Click events on my Buttons. If i add a Click event in the XAML it errors at runtime telling me that i need to compile my XAML.
Is there any way to subscribe to the Click event on MyButton in the Application class?
Well, if you say binding works then why try to mess with the Click event? Rather go with the Button's Command property. Create an ICommand instance (like a DelegateCommand or RelayCommand) that you place in your propertybag and just bind to it!

How to set the DataContext of a KeyBinding to a specific ViewModel?

This seems like such a basic question but after hours of searching around and not figuring out what I'm doing wrong I decided it's time to ask for help!
I'm new to WPF and the MVVM pattern, but am trying to create an application that has several windows you can navigate through by clicking buttons. This is accomplished by having the app window display UserControls using DataTemplates, so there's no content currently shared between pages (though there will be once I create the navigation area). Here's what the XAML looks like for the main window, with there currently only being one page in the application:
<Window x:Class="WPF_Application.ApplicationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF_Application"
Title="ApplicationView" Height="300" Width="300" WindowStartupLocation="CenterScreen" WindowState="Maximized">
<Window.Resources>
<DataTemplate DataType="{x:Type local:LoginMenuViewModel}">
<local:LoginMenuView />
</DataTemplate>
</Window.Resources>
<DockPanel>
<ContentControl
Content="{Binding CurrentPageViewModel}" />
</DockPanel>
Now what I'd like to do is add a KeyBinding that reacts to the escape button being pressed. When this is done "LogoutCommand" should fire in the LoginMenuViewModel. I'm stuck getting the keybinding to trigger any commands within LoginMenuViewModel, and I've figured it's probably because the DataContext needs to be set to reference LoginMenuViewModel. For the life of me I can't get this to work.
Am I going about application-wide commands completely the wrong way? Is there some super simple fix that will make me smack my forehead in shame? Any insight is appreciated!
I do not know your ViewModel code, so it is not easy to give you details hints.
First of all, if your are using MVVM, you should have your own implementation of ICommand interface. You can find here the most common one (and a simple MVVM tutorial too).
After you have your own command class, your ViewModel should expose your LogoutCommand:
public class ViewModel
{
/* ... */
public ICommand LogoutCommand
{
get
{
return /* your command */
}
}
/* ... */
}
In your code behind you will set: DataContext = new ViewModel(); and at this point you can declare the KeyBindings that you need:
<Window.InputBindings>
<KeyBinding Key="Escape" Command="{Binding Path=LogoutCommand, Mode=OneWay}" />
</Window.InputBindings>
In this way when the Window is active and the user press the "Esc" key, your LogoutCommand is executed.
This is a brief summary that I hope will guide you in deepening the MVVM and its command system.

Binding to XAML resource

I am creating a page-based WPF application using MVVM. I have created a custom (non dependency object) helper class to centralize navigation. This class is created as a resource of my main window like so.
<Window.Resources>
<local:NavigationManager x:Key="NavigationManagerKey" x:Name="NavigationManager"/>
</Window.Resources>
The class contains an ICommand that I have exposed publicly so that it can be used in XAML. However, I am struggling to find out how to bind to it. I would prefer not to have to set it as the data context for the page as that is already in use. Normally, I bind to a command like so (when I am binding to a command on the data context)
<Button Header="Image" Command="{Binding CreateImageAssetCommand}"></Button>
Thanks for any help with the matter.
You can set the source of the binding:
<Button Header="Image" Command="{Binding CreateImageAssetCommand, Source={StaticResource NavigationManagerKey}}"></Button>

Issue with Interactivity in MVVMLight

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.

Categories

Resources