WPF. Change DataContext on event binding to access code-behind on a MVVM project - wpf

i'm developing a WPF application with MVVM.
At the XAML code i have a Grid with its DataContext pointing to a ViewModel, and i need to know if it is possible to change the DataContext at runtime to access an event at its code-behind.
Code-behind for the view:
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = new MainViewModel();
InitializeComponent();
}
private void ValidationEvent(object sender, ValidationErrorEventArgs e)
{
//Something useful
}
}
Here is the code that i tried in XAML:
<Grid Validation.Error={Binding Path=ValidationEvent RelativeSource={RelativeSource Self}}/>
The XAML code throws an XamlParseException telling that it is not possible to do the Binding on an "AddErrorHandler", that it is only possible for a DependencyProperty on a DependencyObject.
I don't want to change the DataContext of the Grid because inside it there are elements that access the MainViewModel properties, so i just want to change the DataContext for the Validation.Error event binding... If it is possible...
Thanks.

Validation.Error is an event, not a property. You can't set Bindings to events.
You can use things like MVVM Light's EventToCommand, or Microsoft's own Interactivity EventTrigger to associate Commands to Events.
But there really isn't anything wrong with just adding a regular event handler in code-behind and calling some viewmodel code from there... Contrary to what many people seem to think, MVVM doesn't forbid the use of code-behind and what you'd be doing is not very different from what an EventToCommand or an EventTrigger are doing under the hood.
First of all, just set the event handler name for the Validation.Error event.
<Grid Validation.Error="ValidationEvent" />
And then in your code-behind do whatever you want.
private void ValidationEvent(object sender, ValidationErrorEventArgs e)
{
// Something useful
// Some call to VM code
(this.DataContext as MainViewModel).SomeMethod();
}
This works independently of your DataContext (as long as you cast this.DataContext to the correct type, of course).
Event handlers don't depend on your DataContext, only Bindings do.

Related

How to bind an item command in user conrol to viewmodel command?

I have an UserControl. In my UserControl i have a button that I want bind its command to my ViewModel command. Can I do this?
Yes, you could add a routed event to your user control which gets invoked when the button is pressed.
You can then use various techniques to invoke the view model verb when the user control event fires.
E.g. you could use an attached property, or I would recommend using an MVVM framework such as Caliburn.Micro which has Actions that makes it even more straightforward.
I found it...I can define a DependensyProperty typof RelayCommand in my usercontrol and bind my DependensyProperty to my ViewModel Command
I'm not really sure what you mean but I take a shot.
In your code behind, define a RoutedCommand:
public partial class MyUserControl : UserControl
{
public static RoutedCommand Click =
new RoutedCommand("Click", typeof(UserControl));
}
Then it the xaml, set up a command binding:
<UserControl.CommandBindings>
<CommandBinding
Command="{x:Static MyNameSpace:MyUserControl.Click}"
CanExecute="ClickCanExecute"
Executed="ClickExecuted"/>
</UserControl.CommandBindings>
Then add the handlers in the code behind:
private void ClickCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void ClickExecuted(object sender, ExecutedRoutedEventArgs e)
{
// TODO execution logic goes here
}
Was I close? :)

Binding a ContentControl to a deep path in WPF

The application I'm currently writing is using MVVM with the ViewModel-first pattern. I have XAML similar to the following:
<ContentControl Content="{Binding FooViewModel.BarViewModel.View, Mode=OneWay}"/>
Every VM is a DependencyObject. Every property is a DependencyProperty. Depending upon the state of the application, the value of the BarViewModel property of the FooViewModel can change, thus changing the value of the View property. Unfortunately when this happens, the new view is not displayed, and the old one remains.
This is extremely frustrating. I thought that if any part of a path expression changed, the binding would update, but that doesn't appear to be the case. When I've used shallower path expressions, such as FooViewModel.View and I've changed the value of the FooViewModel property, that has updated the ContentControl to which it's bound, but not in this case.
If your solution is that I abandon ViewModel-first, that is not an option, though I appreciate your advice. I must get this working as is.
CLARIFICATION
This is a question about data binding, and not about MVVM or how to implement it. You can safely ignore the MVVM aspects of this if it helps you to think about the problem, or if you have a different idea about how MVVM should be implemented. This is a large, existing project in which the MVVM design pattern cannot be changed. (It is far too late for that.)
So, with that said, the correct question to be answering is the following:
Given a binding path expression in which every element is a DependencyProperty and the final property is a view bound to a ContentControl, why does a change in a property in the middle of the path not cause the binding to update?
Although I would expect this to work, there are several problems with your approach.
Firstly, your view models should not use DependencyObject or DependencyProperty, this ties them in to WPF. They should instead implement INotifyPropertyChanged. This makes your view models reusable in other presentation technologies such as Silverlight.
Secondly, your view models shouldn't have references to your views, so you shouldn't require a View property on your view models.
I would seriously consider using an MVVM framework for view composition - Caliburn.Micro, for example, makes view model first development extremely straightforward, and already provides a view model base class which implements INotifyPropertyChanged, and a mechanism for building view compositions with conventions.
I.e. you can have a conductor view model which has an ActiveItem property, and you simply place a ContentControl on your view with the same name as the property:
<ContentControl x:Name="ActiveItem" />
You can use the ActivateItem() method to change the current active item.
Caliburn.Micro also has a host of other features, such as being able to place a Button control with x:Name="Save" on your view, and your Save method on your view model will automatically be invoked when the button is clicked.
Every VM is a DependencyObject. Every property is a
DependencyProperty.
why? a viewmodel should be a simple class with INotifyPropertyChanged and the Properties should be simple properties.
and if you want your different viewmodel be rendered in a different way - you should use DataTemplate.
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyViewModelA}>
<MyViewA/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyViewModelB}>
<MyViewB/>
</DataTemplate>
</Windows.Resources>
<Grid>
<ContentControl Content="{Binding MyActualVM}"/>
</Grid>
</Window>
EDIT: btw you always bind to the last Property: FooViewModel.BarViewModel.View --> so the INotifyPropertyChanged (if raised) just work for the .View
EDIT2: another approach could be to get the BindingExpression of your content control and call.
System.Windows.Data.BindingExpression expr = //get it from your contentcontrol
expr.UpdateTarget();
EDIT3: and a simple mvvm way - just use INotifyPropertyChanged
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MyFooVM = new FooVM();
this.MyFooVM.MyBarVM = new BarVM(){View = "erster"};
this.DataContext = this;
}
public FooVM MyFooVM { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
this.MyFooVM.MyBarVM = new BarVM(){View = "zweiter"};
}
}
public class INPC : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChanged(string property)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(property));
}
#endregion
}
public class FooVM:INPC
{
private BarVM _myBarVm;
public BarVM MyBarVM
{
get { return _myBarVm; }
set { _myBarVm = value;OnPropChanged("MyBarVM"); }
}
}
public class BarVM : INPC
{
private string _view;
public string View
{
get { return _view; }
set { _view = value;OnPropChanged("View"); }
}
}

hang on an handler to an event handler in View

i want to add an event to my Event handler. The Problem is my EventHandler is in my ViewModel and my Event is in View. How can I access my ViewModel from View to hang on this event?
thanks for your help
Since the view model is set as DataContext of the view you can subscribe to the DataContextChanged of the view and in the handler cast the DataContext to the view model's type and attach the desired event handler. Something like this:
public class MyView : UserControl
{
public MyView()
{
InitializeComponent();
DataContextChanged += OnDataContextChanged;
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
MyViewModel viewModel = DataContext as MyViewModel;
if (viewModel != null)
{
viewModel.MyEvent += OnMyEvent;
}
}
}
Usually in MVVM, when the View wants to defer some operation to the ViewModel it does so by binding to an ICommand. This is essentially the same usage model as with the event, as the View "pushes" the notification to the ViewModel.
Depending on the nature of the event I would suggest you look into doing it with an ICommand, since:
The whole idea behind MVVM is for the ViewModel to not assume that the View is "real" UI
Typically, however, the View is real UI (so the event you mention probably has to do with something happening in the UI)
Which leads me to think that you are searching for a way to have the ViewModel subscribe to a UI event from the View, something that would come contrary to the premise of MVVM.
Is invoking a command on the ViewModel problematic in your case?

Key press inside of textbox MVVM

I am just getting started with MVVM and im having problems figuring out how I can bind a key press inside a textbox to an ICommand inside the view model. I know I can do it in the code-behind but im trying to avoid that as much as possible.
Update: The solutions so far are all well and good if you have the blend sdk or your not having problems with the interaction dll which is what i'm having. Is there any other more generic solutions than having to use the blend sdk?
First of all, if you want to bind a RoutedUICommand it is easy - just add to the UIElement.InputBindings collection:
<TextBox ...>
<TextBox.InputBindings>
<KeyBinding
Key="Q"
Modifiers="Control"
Command="my:ModelAirplaneViewModel.AddGlueCommand" />
Your trouble starts when you try to set Command="{Binding AddGlueCommand}" to get the ICommand from the ViewModel. Since Command is not a DependencyProperty you can't set a Binding on it.
Your next attempt would probably be to create an attached property BindableCommand that has a PropertyChangedCallback that updates Command. This does allow you to access the binding but there is no way to use FindAncestor to find your ViewModel since the InputBindings collection doesn't set an InheritanceContext.
Obviously you could create an attached property that you could apply to the TextBox that would run through all the InputBindings calling BindingOperations.GetBinding on each to find Command bindings and updating those Bindings with an explicit source, allowing you to do this:
<TextBox my:BindingHelper.SetDataContextOnInputBindings="true">
<TextBox.InputBindings>
<KeyBinding
Key="Q"
Modifiers="Control"
my:BindingHelper.BindableCommand="{Binding ModelGlueCommand}" />
This attached property would be easy to implement: On PropertyChangedCallback it would schedule a "refresh" at DispatcherPriority.Input and set up an event so the "refresh" is rescheduled on every DataContext change. Then in the "refresh" code just, just set DataContext on each InputBinding:
...
public static readonly SetDataContextOnInputBindingsProperty = DependencyProperty.Register(... , new UIPropetyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
var element = obj as FrameworkElement;
ScheduleUpdate(element);
element.DataContextChanged += (obj2, e2) =>
{
ScheduleUpdate(element);
};
}
});
private void ScheduleUpdate(FrameworkElement element)
{
Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
{
UpdateDataContexts(element);
})
}
private void UpdateDataContexts(FrameworkElement target)
{
var context = target.DataContext;
foreach(var inputBinding in target.InputBindings)
inputBinding.SetValue(FrameworkElement.DataContextProperty, context);
}
An alternative to the two attached properties would be to create a CommandBinding subclass that receives a routed command and activates a bound command:
<Window.CommandBindings>
<my:CommandMapper Command="my:RoutedCommands.AddGlue" MapToCommand="{Binding AddGlue}" />
...
in this case, the InputBindings in each object would reference the routed command, not the binding. This command would then be routed up the the view and mapped.
The code for CommandMapper is relatively trivial:
public class CommandMapper : CommandBinding
{
... // declaration of DependencyProperty 'MapToCommand'
public CommandMapper() : base(Executed, CanExecute)
{
}
private void Executed(object sender, ExecutedRoutedEventArgs e)
{
if(MapToCommand!=null)
MapToCommand.Execute(e.Parameter);
}
private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute =
MapToCommand==null ? null :
MapToCommand.CanExecute(e.Parameter);
}
}
For my taste, I would prefer to go with the attached properties solution, since it is not much code and keeps me from having to declare each command twice (as a RoutedCommand and as a property of my ViewModel). The supporting code only occurs once and can be used in all of your projects.
On the other hand if you're only doing a one-off project and don't expect to reuse anything, maybe even the CommandMapper is overkill. As you mentioned, it is possible to simply handle the events manually.
The excellent WPF framework Caliburn solves this problem beautifully.
<TextBox cm:Message.Attach="[Gesture Key: Enter] = [Action Search]" />
The syntax [Action Search] binds to a method in the view model. No need for ICommands at all.
Perhaps the easiest transition from code-behind event handling to MVVM commands would be Triggers and Actions from Expression Blend Samples.
Here's a snippet of code that demonstrates how you can handle key down event inside of the text box with the command:
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<si:InvokeDataCommand Command="{Binding MyCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
The best option would probably be to use an Attached Property to do this. If you have the Blend SDK, the Behavior<T> class makes this much simpler.
For example, it would be very easy to modify this TextBox Behavior to fire an ICommand on every key press instead of clicking a button on Enter.

WPF User Control's DataContext is Null

I have a user control where the XAML of the control can bind to the appropriate properties from the parent's data context like normal (the data context propagates in xaml).
For example, I have a window whose DataContext I am setting to ObjectA for example. My user control within the window is then try to access the properties within the dataContext
So my window's xaml and code behind can both see a non-null DataContext.
My control that DataContext propagates to can see a non-null DataContext in the Xaml but not in the code behind.
What is the proper way of handling this?
failing that if you need to check whether the DataContext is being set you can use the DataContextChanged
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
DataContextChanged += new DependencyPropertyChangedEventHandler(UserControl1_DataContextChanged);
}
void UserControl1_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// You can also validate the data going into the DataContext using the event args
}
}
Note it wont enter UserControl1_DataContextChanged until DataContext is changed from null to a different value.
Not sure if this answers your question but can be quite handy to use in debugging issues.
I think you are checking the 'DataContext' in the constructor of the UserControl. It will be null at the Constructor since the user control hasnt yet created while execution is in the constructor code. But check the property at Loaded event you will see the object properly.
public partial class UserControl1
{
public UserControl1()
{
this.InitializeComponent();
//DataContext will be null here
this.Loaded += new RoutedEventHandler(UserControl1_Loaded);
}
void UserControl1_Loaded(object sender, RoutedEventArgs e)
{
//Check DataContext Property here - Value is not null
}
}
I would check to see whether you are having a binding error at runtime. Add this namespace to your XAML:
xmlns:debug="clr-namespace:System.Diagnostics;assembly=System"
and check the debugger's Output window for relevant error messages.
Alternatively, can you show us more code?

Resources