WPF Call Method On User Control With Binding - wpf

I created a Task List control. I have an AddTask method on it. I'd like to call this method from the host Window.
I found a few posts here in SO and other sites that suggest using an interface, then looping over all the controls in the window to find the control, then getting a reference to it and using that to call the method. Here's an example:
Call method on various user controls
But is it possible to call a method somehow with binding? Assume someone is using MVVM and the Window's VM wants to fire the control's AddTask method. is this possible?
Thanks!

If you really want to do it (in a possible) the right way i'd tell you to write about MVVM.
Binding and methods work very well in MVVM using Commands
Here it is my solution
Create a ViewModel class
Create a nested class MyCommandBehaviour that implements ICommand (some people create the class in a different class)
In the view model create a property MyCommandBehaviour MyCommand
In the constructor of the view model instantiate that property
In The XAML bind the button {Binding MyCommand}
Set the DataContext of the window (or user control) to the view model
Note: I usually create the Command nested class with a constructor that accepts the 'parent' view model. Because the class is nested it can directly access the view model private members
public class OkCommand : System.Windows.Input.ICommand
{
private MyViewModel _vm;
public OkCommand(MyViewModel vm)
{
this._vm = vm;
}
public bool CanExecute(object parameter)
{
return true;//I never use this and the event below
}
#pragma warning disable 0067
public event EventHandler CanExecuteChanged;
#pragma warning restore 0067
public void Execute(object parameter)
{
//do your stuff. Note you can access the MyViewModel members here via _vm
}
}

Related

Prism5 PopupWindowAction and injection

I looked at part of InteractivityQuickstart official example.
<prism:InteractionRequestTrigger SourceObject="{Binding ItemSelectionRequest, Mode=OneWay}">
<prism:PopupWindowAction>
<prism:PopupWindowAction.WindowContent>
<views:ItemSelectionView />
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
So, ItemSelectionRequest called less-parametre constructor
public ItemSelectionView()
{
this.DataContext = new ItemSelectionViewModel();
InitializeComponent();
}
in code-behind of ItemSelectionView.
Questions:
1) How possible to set DataContext without "new", because
public ItemSelectionView(ItemSelectionViewModel model)
or
[Dependency]
public ItemSelectionViewModel ViewModel
{
set { this.DataContext = value; }
}
doesn`t work.
I need to get some services in ViewModel => i need to call something like this
public ItemSelectionViewModel(IEventAggregator eventAggregator)
{
_eventAggregator=eventAggregator;
}
If you need a service for your Popup ViewModel, you could get it by using the ServiceLocator.
public ItemSelectionView()
{
InitializeComponent();
DataContext = ServiceLocator.Current.GetInstance<ItemSelectionViewModel>();
}
Rather than use the ServiceLocator to set your ViewModel as Brian Lagunas suggests, why not have a parameterless constructor for ViewModel, set the ViewModel directly in your View class (XAML or code-behind), and use the ServiceLocator within the ViewModel itself to get the services (or their interfaces) your ViewModel needs? I suggest this for two reasons:
Using the ServiceLocator in the constructor of the View for the popup will give you an error of "ServiceLocationProvider must be set" at design time within the "prism:PopupWindowAction.WindowContent" section. (Though it works fine at runtime.)
You've already been forced into a situation where you have to bypass dependency injection in some manner, so why not simplify the code, particularly if you only need access to one service anyway.
So you can do something like this:
public ItemSelectionViewModel()
{
_eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
}
And if you only need to use the IEventAggregator object once, there's no reason to even assign it to a field. Just use the ServiceLocator call where you need to get the Event Aggregator and remove your explicit constructor entirely.

Bind Control to Two properties

Using MVVM pattern and WPF, I would like to bind my controls to two properties. Let's say I have a label that is bind to property on VM1 and I would like to bind it as well to the property on VM2 to send received value from VM1 to VM2.
I could use messenger and for multiple items Tuple Class, but I was wondering if there is another solution for that. Multibinding? but then I need ValueConverter.
Thank you for any suggestions.
Since The View-Model is an abstraction of the view that exposes public properties and commands, it doesn't make a lot of sense for a view to have two view-models the way you explained. It'll be more rational if there is a VM class as the view-model of your view that has two properties of type VM1 and VM2. Then the binding would be on VM.VM1.YourText and you can notify VM2 via events like this:
in VM1:
public event EventHandler<EventArgs> ValueChanged;
string _yourText;
public string YourText
{
get
{
return _yourText;
}
set
{
_yourText= value;
if (ValueChanged != null)
ValueChanged(_yourText, new EventArgs());
}
}
In VM:
public VM1 Vm1 {get; set;}
public VM2 Vm2 {get; set;}
public VM()
{
InitializeComponent();
Vm1 = new VM1();
Vm2 = new VM2();
Vm1.ValueChanged += Item_ValueChanged;
DataContext = this;
}
void Item_ValueChanged(object sender, EventArgs e)
{
VM2.YourAnotherText = sender.ToString();
}
If 2 properties are connected, usually INotifyPropertyChanged can be utilized to notify about a change of 2 or more properties if on the same ViewModel.
What I understand is that you want also to notify a View attached to a ViewModel about a change of a property on another ViewModel. This is usually done by letting ViewModels exchange information.
If that's a one rare case, using message bus for that might be an overkill. Usually keeping a reference to each view model and changing properties from outside should be all right. To keep separation of concerns you can create an interface on one or both viewmodels and reference this interface instead of a concrete type.
Overall keeping a single binding between a control and property keeps it simple and easy to understand and you should worry about making sure that this property handles all changes to/from other VMs.

Binding to base viewmodel command from View

I have a base viewModel and two derived viewModels from it. In my base viewModel I have some commands CloseCommand, CancelCommand etc.
My View is attached to one of the derived viewModels. I need to bind a button to the CloseCommand in the base viewModel.
How can I do this with inheritance?
I have bind the button's content with string property from base viewModel and its working fine but how can I bind a command?
There is nothing special u need to do to bind those commands as far as they are exposed as Public properties of your ViewModel. I had the same situation so here is my very owne implementation of how I did that.
First of all in your base class define OKCommand / CancelCommand of type ICommand. as far es Execute and CanExecute methods are concerned, I have them defined as protected virtual Methods (By The way u can also define your Commands as Virtual. this will give u ability to write XAML style which sets visibility mode of button to collapsed if its Command Value is null). Inside your derived ViewModels u simply override commands, Execute and CanExecute methods as needed but from your view u always simply bind to command names directly.
below is an example of what I have just explained to u.
public abstract class ViewModelbase
{
private DelegateCommand _okCommand;
public virtual DelegateCommand OkCommand
{
get { return _okCommand ?? (_okCommand = new DelegateCommand(OkExecuteCommand, CanOkExecute)); }
}
protected virtual void OkExecuteCommand()
{
DialogResult = true;
}
protected virtual bool CanOkExecute()
{
return IsValid;
}
}
Then u simply define your concrete ViewModel classes which are derived from base ViewModel class
public class SampleViewModel : ViewModelbase
{
//If u have defined XAML style which sets viability of button as collapsed if its command value is null u simply override command
public override DelegateCommand OkCommand { get { return null; } }
protected override void OkExecuteCommand()
{
do whatever u want as this is a command execution
}
}
in XAML part u do not have to do anything special just bind your buttons command to Viewmodel Command as you would do if there was no Base class.
The key point here is that you Should expose your commands from your base ViewModel class with public modifier (u need only getter so this code provides sample of one way u can expose commands)
Nothing special, here's what you need to do:
Set the DataContext of your View to your derived ViewModel
Ensure that the CloseCommand, for example, is declared as a public property in your ViewModelBase
Set the Button's Command property to "{Binding CloseCommand}"

Passing data to user control in MVVM pattern

Background:
I have WPF application with a main window containing a user control. I want to pass a value from the main window to the user control.
In the Main window's constructor I have code:
public MainWindow()
{
InitializeComponent();
_vm = new MainWindowViewModel();
this.DataContext = _vm;
ucControl = new UserControl1("NameSet");
}
(ucControl is my user control)
User control has two constructors:
public UserControl1()
{
InitializeComponent();
this.ID = ID.GetNewID;
}
public UserControl1(string name)
{
InitializeComponent();
_vm = new UCViewModel(name);
this.DataContext = _vm;
this.ID = ID.GetNewID;
}
The problem is: although the second constructor (with parameter) is called, it is not loaded in the main window. I checked the ID (this.ID) in the user control's loaded event and I see the ID set in the default constructor and its DataContext is null. Because of this reason, I do not get the "name" string in my user control.
Any help please? Since I am using MVVM pattern I do not want to expose properties in user control (view) to be set from main window just for this.
You are instantiating the UserControl1 object twice:
Once within the XAML. The <uc:UserControl1> element instantiates a UserControl1 object, using the default constructor, and assigns it to the member ucControl.
You instantiate it again within the constructor of the MainWindow object
If you put a break point in the constructor of UserControl, you'll notice it is called twice. I assume WPF instantiate and initialize the XAML's UserControl (#1 from above) after you assign the dynamic UserControl (#2 from above), and this is why you see the former in the logical tree of MainWindow.
You should have only one instance. If you want to parameterized a user control, the canonical paradigm is what you mention that you don't want to do (why??). If you had such a property, you could set it in the XAML: <uc:UserControl1 x:Name="..." YourProperty="NameSet>
exposing such a property is a single line in the UserControl:
public YourProperty { get; set; }
If you insist of not having this line, you should do the following:
Remove the XAML's user control.
In main window, subscribe to the Loaded event
In the handler of the Loaded event, instantiate a new UserControl1 - with whatever constructor parameter that you want.
Manually add it to the Children array of the parent Grid element
Clearly this isn't my recommendation. In addition to the complexity, with the former method you'll also work very well with the Visual Studio designer.

How to open a new window using MVVM Light Toolkit

I am using MVVM Light toolkit in my WPF application. I would like to know what is the best approach for opening a new window from an existing window. I have got this MainViewModel, which is responsible for MainWindow of my application. Now in the MainView, on a button click, I would like to open a second window on top of it. I have got RelayCommmand binded to the Button's Command. In the RelayCommand's method, I can create a new window object and simply call Show(), something like this:
var view2 = new view2()
view2.Show()
but I don't think the ViewModel should be responsible for creating the new view2 object. I have read this post WPF MVVM Get Parent from VIEW MODEL where Bugnion has suggested to pass message to the view1 from the viewmodel1 and then view1 should create the new view2. But I am not sure what does he actually mean by passing the message to the view1? How should the view1 handle the message? In it's code behind or what?
Regards,
Nabeel
Passing a message from ViewModel1 to View1 means to use the messaging capabilities in the MVVM Light Toolkit.
For example, your ViewModel1 could have a command called ShowView2Command, then it would send a message to display the view.
public class ViewModel1 : ViewModelBase
{
public RelayCommand ShowView2Command { private set; get; }
public ViewModel1() : base()
{
ShowView2Command = new RelayCommand(ShowView2CommandExecute);
}
public void ShowView2CommandExecute()
{
Messenger.Default.Send(new NotificationMessage("ShowView2"));
}
}
View1 would register to receive messages in its code behind and display View2 when it receives the correct message.
public partial class View1 : UserControl
{
public View1()
{
InitializeComponent();
Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
}
private void NotificationMessageReceived(NotificationMessage msg)
{
if (msg.Notification == "ShowView2")
{
var view2 = new view2();
view2.Show();
}
}
}
Why do you go this route? Its simple. If you replace your button with a toggleButton, or a hyperlink, or any other number of button-like controls, you don't need to update your "code behind" - its a basic principle of the MVVM pattern. In your new toggleButton (or whatever), you still end up binding to the same exact Command.
For example, I'm creating a project for a client who wants to have 2 UI's - one is going to be fundamentally different in every way, in terms of presentation. Horizontal tabs vs Vertical RadPanelBar (think Accordion) for navigation. Both of these views can point to the same viewModel - when a user clicks the Work Order tab in View 1, it fires the same "WorkOrderCommand" that's fired in the Work Order Header in the panel bar.
In a code-behind model, you'd have to code two separate events. Here you only have to code one.
Furthermore, it allows a designer using Blend to create any layout they want. As long as they have the hooks (EventToCommand control) in place, myself (as a developer) couldn't care less what the final product looks like.
Loose coupling is incredibly powerful.
You can do in this way like you need to create some events and register those in view and call these in view model.and open that pop up window.
Like This example
public class Mainclass : MainView
{
public delegate abc RegisterPopUp(abc A);
public RegisterPopUp POpUpEvent ;
public RelayCommand ShowCommand { private set; get; }
public void ShowCommand()
{
ShowCommand("Your parameter");
}
}
inside the view MainView mn=new MainView();
Register the event here like thake mn.POpUpEvent += than click on tab button double time
and in registers popup method right the code for opening the pop up window.
Unless I am missing the point here - if I were to use the code behind, then why not directly implement button_click event and open the second view?
What Bugnion seems to be suggesting is view1 -> button click -> relay command -> viewmodel1 -> message -> view1 -> view1.cs -> open view 2.
You are going to sacrifice testability anyhow by writing code-behind, so why take such a long route?
You can abstract the view specific features into services using generic interface. In the view layer you can provide concrete instances of these services and build view models using the IoC container and Dependency Injection technique.
In your case you can build an interface IWindowManager or something similar which has the required method. This can be implmented in your view layer. I wrote a small blog post recently demonstrating how to abstract the dialog behaviour out of view model. Similar apporach can be used for any user interface related service like Navigation, MessageBoxes etc.
This link might be helpful for you http://nileshgule.blogspot.com/2011/05/silverlight-use-dialogservice-to.html
Many people also use the approach of firing events from view models which are subscribed on the view.cs file and from there the MessageBox or any other UI related action is performed. I personally like the approach of injecting services because then you can provide multiple implementations of the same service. A simple example would be how navigation is handled in Silverlight and Windows Phone 7 applications. You can use the same view model but inject different implementations of the Navigation service based on the application type.
I find the best way to approach this, is opening and closing the window from the ViewModel. As this link suggests,
Create a DialogCloser class
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(DialogCloser), new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null) window.Close();
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
Create a Base ViewModel inheriting from GalaSoft.MvvmLight.ViewModelBase with there additional members. Once done, use this viewmodel as base for other viewmodels.
bool? _closeWindowFlag;
public bool? CloseWindowFlag
{
get { return _closeWindowFlag; }
set
{
_closeWindowFlag = value;
RaisePropertyChanged("CloseWindowFlag");
}
}
public virtual void CloseWindow(bool? result = true)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() =>
{
CloseWindowFlag = CloseWindowFlag == null ? true : !CloseWindowFlag;
}));
}
In the view, Bind the DialogCloser.DialogResult dependency property with the CloseWindowFlag property in the base viewmodel.
Then you can open/close/hide the window from the viewmodel.

Resources