How to databind Click= to a function on the object and not the page - wpf

I'm using Silverlight, but I'd be interested in a WPF answer as well
I have a list that is databound to an linked list of “Favorites”. Each favorite contains a name and a phone number.
The list is bound to a DataTemplate that describes the graphical aspects. In the this template is a button – Dial. When you click on that button I want the Dial() method of the Favorite to be called. Right now the Dial method of the page/window is called.
If this is not possible is there a way I can get the Favorite to somehow be attached to the Button? such that I know which Favorite was associated with the button press?
the below XAML does not work, Text="{Binding Name}" works great as it binds to the Name property on the Favorite, but Click="{Binding Dial}" does not call Dial() on the Favorite.
<DataTemplate x:Key="DataTemplate1">
<StackPanel d:DesignWidth="633" Orientation="Horizontal" Height="93">
<Button x:Name="DialButton" Content="Edit" Click="{Binding Dial}"/>
<TextBlock x:Name="TextBlock" TextWrapping="Wrap" Text="{Binding Name}" FontSize="64" Height="Auto" FontFamily="Segoe WP SemiLight"/>
</StackPanel>
</DataTemplate>

So it should go:
<Button CommandParameter="{Binding}" Command="{Binding Dial}"/>
Then you will receive the data object as the command parameter. In this scenario you must provide a Property that is called Dial and returns an ICommand-implementation. If the property is not available on your data-object but on the main class (code-behind), you must look for it within the binding, use for this the RelativeSource keyword.
Another way is to make a click handler. In the click handler you can cast the sender to a Button (or FrameworkElement) and then get the data object from the DataContext. I assume you tried to create such a solution.
private void Button_Click(object sender, RoutedEventArgs e) {
Button btn = (Button)sender;
MyObject obj = btn.DataContext as MyObject;
if(null != obj){
obj.Dial();
// or Dial(obj);
}
}
The markup must be as follows:
<Button x:Name="DialButton" Content="Edit" Click="Button_Click"/>
The main difference is, that I removed the binding from the Click-Event and registered an event-handler.
A third solution would be, to register a handler in the code behind for the Button.ClickEvent. The principle is similiar as in the second example.
I don't know silverlight very well. Perhaps there are the things a little bit other.

HappyClicker's first solution is the best one for most purposes, since it supports good design patterns such as MVVM.
There is another simple way to get the same result using an attached property, so you can write:
<Button Content="Edit" my:RouteToContext.Click="Edit" />
and the Edit() method will be called on the button's DataContext.
Here is how the RouteToContext class might be implemented:
public class RouteToContext : DependencyObject
{
public static string GetClick(FrameworkElement element) { return (string)element.GetValue(ClickProperty); }
public static void SetClick(FrameworkElement element, string value) { element.SetValue(ClickProperty, value); }
public static DependencyProperty ClickProperty = ConstructEventProperty("Click");
// Additional proprties can be defined here
private static DependencyProperty ConstructEventProperty(string propertyName)
{
return DependencyProperty.RegisterAttached(
propertyName, typeof(string), typeof(RouteToContext),
new PropertyMetadata
{
PropertyChangedCallback = (obj, propertyChangeArgs) =>
obj.GetType().GetEvent(propertyName)
.AddEventHandler(obj, new RoutedEventHandler((sender, eventArgs) =>
((FrameworkElement)sender).DataContext
.GetType().GetMethod((string)propertyChangeArgs.NewValue)
.Invoke(((FrameworkElement)sender).DataContext,
new object[] { sender, eventArgs }
)
))
}
);
}
}
How it works: When the RouteToContext.Click attached property is set, Type.GetEvent() is used to find the event named "Click", and an event handler is added to it. This event handler uses Type.GetMethod() to find the specified method on the DataContext, then invokes the method on the DataContext, passing the same sender and eventArgs it received.

Related

WPF/XAML: Shortcut to a user control

I have created a UserControl named ContactPerson. It contains a persons name, phone number etc.
This usercontrol does not have a headline (ie. a label such as "_Contact Person") because I use this usercontrol in different situations.
However in one situation, I do have such a label, which means my code look somewhat like this:
<Label Content="_Contact Person"
Target="{Binding ElementName=_contactView}" />
<View1:contactView x:Name="_contactView"
DataContext="{Binding SupplierContact}"/>
I want - to set keyboard focus to the name-textbox inside the ContactPersonUserControl but it seems to be a difficult task (it is private after alle).
I do not want to move the label inside the usercontrol, which I guess would in fact be the most simple solution. It seems to me that XAML should provide a solution to this scenario.
How to do this in a simple and elegant way?
Thx
(I have a few of these controls, so I'll need to use the same solution several times).
I am not sure whether i understand your scenario correctly, But if you want to set focus to ui control from view model, then you may need to create an attached property.
Attached Property:
public static class ControlFocusExtension
{
public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(ControlFocusExtension), new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
public static bool GetIsFocused(DependencyObject obj)
{
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value)
{
obj.SetValue(IsFocusedProperty, value);
}
private static void OnIsFocusedPropertyChanged(DependencyObject pDependencyObject, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)pDependencyObject;
if ((bool)e.NewValue)
{
uie.Focus();
}
}
}
User control XAML:
Now in your View (in XAML) you can bind this property to your ViewModel:
<TextBox local:FocusExtension.IsFocused="{Binding IsNameFocused}" />
You can change the value of IsNameFocused from your view model if needed.
Alternatively if you don't want to bind this then you can use it as -
<TextBox local:FocusExtension.IsFocused="True" />
Set focusvisualstyle property null for those you dont want to set focus like this
example <Label FocusVisualStyle="{x:Null}">
and for textbox focus <TextBox Focusable="True"/>

Why doesn't this Binding work

I have a 3rd party SplitButton control that exposes some DropDownContent and a boolean IsOpen dp to control whether the drop down content is shown or not.
In the case the DropDownContent is a StackPanel with several Buttons, each of which is bound to a command in the view model. In addition to executing that command, clicking the button needs to close the open DropDown content, which I am doing with the AttachedBehavior below.
But my binding, which simple needs to get a reference to the ancestor SplitButton control doesn't work. In the binding, you will note I am trying to Find the first Ancestor control of type SplitButton. I do see however that the debug info says ancestor level 1, so I changed the level to as high as 4, but still with an error.
Can someone see what the fix is?
binding error
System.Windows.Data Error: 4 : Cannot find source for binding with reference
'RelativeSource FindAncestor, AncestorType='Xceed.Wpf.Toolkit.SplitButton',
AncestorLevel='1''. BindingExpression:(no path); DataItem=null; target element is
'CloseDropDownContentBehavior' (HashCode=8896066); target property is 'DropDownButtonElement' (type 'SplitButton')
xaml
<DataTemplate x:Key="AddNewPartyTemplate">
<StackPanel HorizontalAlignment="Right" Margin="10">
<toolkit:SplitButton x:Name="theSplitButton" Content="{resx:Resx Subject_AddNewWithChoices}">
<toolkit:SplitButton.DropDownContent>
<StackPanel x:Name="theStackPanel">
<Button Content="{resx:Resx Person}" Command="{Binding AddNewPersonCommand}"
>
<i:Interaction.Behaviors>
<local:CloseDropDownContentBehavior
*** DropDownButtonElement="{Binding
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type toolkit:SplitButton}}}"/>
</i:Interaction.Behaviors>
</Button>
...
</StackPanel>
</toolkit:SplitButton.DropDownContent>
</toolkit:SplitButton>
</StackPanel>
</DataTemplate>
attached behavior
public class CloseDropDownContentBehavior : Behavior<ButtonBase>
{
private ButtonBase _button;
protected override void OnAttached()
{
_button = AssociatedObject;
_button.Click += OnPartyButtonClick;
}
protected override void OnDetaching()
{
_button.Click -= OnPartyButtonClick;
}
// **** the point of it all
void OnPartyButtonClick(object sender, RoutedEventArgs e) { DropDownButtonElement.IsOpen = false; }
public static readonly DependencyProperty DropDownButtonElementProperty =
DependencyProperty.Register("DropDownButtonElement",
typeof(SplitButton), typeof(CloseDropDownContentBehavior), new UIPropertyMetadata(null, OnDropDownElementChanged));
public DropDownButton DropDownButtonElement
{
get { return (DropDownButton)GetValue(DropDownButtonElementProperty); }
set { SetValue(DropDownButtonElementProperty, value); }
}
private static void OnDropDownElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
}
}
Guessing it's because Interaction.Behaviors isn't part of the visual tree, so the binding won't find the ancestor. Have you tried simply:
DropDownElement="{Binding ElementName=theSplitButton}"
Update from comments: the solution in this case is to simply use x:Reference:
DropDownElement="{x:Reference theSplitButton}"
i dont know the SplitButton.DropDownContent but if its behave like a context menu the following answer might help: WPF context menu whose items are defined as data templates
this trick is to bind with RelativeSource Self or Type ContextMenu and then set the Path to PlacementTarget.DataContext.YourProperty

Is there a way to call external functions from xaml?

Is there any way to call methods of external objects (for example resource objects) directly from xaml?
I mean something like this:
<Grid xmlns:dm="clr-namespace:MyNameSpace;assembly=MyAssembly">
<Grid.Resources>
<dm:TimeSource x:Key="timesource1"/>
</Grid.Resources>
<Button Click="timesource_updade">Update time</Button>
</Grid>
The method timesource_update is of course the method of the TimeSource object.
I need to use pure XAML, not any code behind.
Check this thread, it has a similar problem. In general you can't call a method directly from xaml.
You could use Commands or you can create an object from xaml which will create a method on a thread, which will dispose itself when it needs.
But I am afraid you can't do it just in pure XAML. In C# you can do everything you can do in XAML, but not other way round. You can only do some certain things from XAML that you can do in C#.
OK, here is the final sollution.
XAML:
<Grid xmlns:dm="clr-namespace:MyNameSpace;assembly=MyAssembly">
<Grid.Resources>
<dm:TimeSource x:Key="timesource1"/>
</Grid.Resources>
<Button Command="{x:Static dm:TimeSource.Update}"
CommandParameter="any_parameter"
CommandTarget="{Binding Source={StaticResource timesource1}}">Update time</Button>
</Grid>
CODE in the TimeSource class:
public class TimeSource : System.Windows.UIElement {
public static RoutedCommand Update = new RoutedCommand();
private void UpdateExecuted(object sender, ExecutedRoutedEventArgs e)
{
// code
}
private void UpdateCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
// Constructor
public TimeSource() {
CommandBinding cb = new CommandBinding(TimeSource.Update, UpdateExecuted, UpdateCanExecute);
CommandBindings.Add(cb2);
}
}
TimeSource has to be derived from UIElement in order to have CommandBindings. But the result is calling outer assembly method directly from XAML. By clicking the button, 'UpdateExecuted' method of the object timesource1 is called and that is exactly what I was looking for.

How to bind Close command to a button

The easiest way is to implement ButtonClick event handler and invoke Window.Close() method, but how doing this through a Command binding?
All it takes is a bit of XAML...
<Window x:Class="WCSamples.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close"
Executed="CloseCommandHandler"/>
</Window.CommandBindings>
<StackPanel Name="MainStackPanel">
<Button Command="ApplicationCommands.Close"
Content="Close Window" />
</StackPanel>
</Window>
And a bit of C#...
private void CloseCommandHandler(object sender, ExecutedRoutedEventArgs e)
{
this.Close();
}
(adapted from this MSDN article)
Actually, it is possible without C# code. The key is to use interactions:
<Button Content="Close">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
In order for this to work, just set the x:Name of your window to "window", and add these two namespaces:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
This requires that you add the Expression Blend SDK DLL to your project, specifically Microsoft.Expression.Interactions.
In case you don't have Blend, the SDK can be downloaded here.
I think that in real world scenarios a simple click handler is probably better than over-complicated command-based systems but you can do something like that:
using RelayCommand from this article http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
public class MyCommands
{
public static readonly ICommand CloseCommand =
new RelayCommand( o => ((Window)o).Close() );
}
<Button Content="Close Window"
Command="{X:Static local:MyCommands.CloseCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}"/>
If the window was shown with Window.ShowDialog():
The simplest solution that I know of is to set the IsCancel property to true of the close Button:
<Button Content="Close" IsCancel="True" />
No bindings needed, WPF will do that for you automatically!
This properties provide an easy way of saying these are the "OK" and "Cancel" buttons on a dialog. It also binds the ESC key to the button.
Reference: MSDN Button.IsCancel property.
For .NET 4.5 SystemCommands class will do the trick (.NET 4.0 users can use WPF Shell Extension google - Microsoft.Windows.Shell or Nicholas Solution).
<Window.CommandBindings>
<CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}"
CanExecute="CloseWindow_CanExec"
Executed="CloseWindow_Exec" />
</Window.CommandBindings>
<!-- Binding Close Command to the button control -->
<Button ToolTip="Close Window" Content="Close" Command="{x:Static SystemCommands.CloseWindowCommand}"/>
In the Code Behind you can implement the handlers like this:
private void CloseWindow_CanExec(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void CloseWindow_Exec(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.CloseWindow(this);
}
In the beginning I was also having a bit of trouble figuring out how this works so I wanted to post a better explanation of what is actually going on.
According to my research the best way to handle things like this is using the Command Bindings. What happens is a "Message" is broadcast to everything in the program. So what you have to do is use the CommandBinding. What this essentially does is say "When you hear this Message do this".
So in the Question the User is trying to Close the Window. The first thing we need to do is setup our Functions that will be called when the SystemCommand.CloseWindowCommand is broadcast. Optionally you can assign a Function that determines if the Command should be executed. An example would be closing a Form and checking if the User has saved.
MainWindow.xaml.cs (Or other Code-Behind)
void CloseApp( object target, ExecutedRoutedEventArgs e ) {
/*** Code to check for State before Closing ***/
this.Close();
}
void CloseAppCanExecute( object sender, CanExecuteRoutedEventArgs e ) {
/*** Logic to Determine if it is safe to Close the Window ***/
e.CanExecute = true;
}
Now we need to setup the "Connection" between the SystemCommands.CloseWindowCommand and the CloseApp and CloseAppCanExecute
MainWindow.xaml (Or anything that implements CommandBindings)
<Window.CommandBindings>
<CommandBinding Command="SystemCommands.CloseWindowCommand"
Executed="CloseApp"
CanExecute="CloseAppCanExecute"/>
</Window.CommandBindings>
You can omit the CanExecute if you know that the Command should be able to always be executed Save might be a good example depending on the Application. Here is a Example:
<Window.CommandBindings>
<CommandBinding Command="SystemCommands.CloseWindowCommand"
Executed="CloseApp"/>
</Window.CommandBindings>
Finally you need to tell the UIElement to send out the CloseWindowCommand.
<Button Command="SystemCommands.CloseWindowCommand">
Its actually a very simple thing to do, just setup the link between the Command and the actual Function to Execute then tell the Control to send out the Command to the rest of your program saying "Ok everyone run your Functions for the Command CloseWindowCommand".
This is actually a very nice way of handing this because, you can reuse the Executed Function all over without having a wrapper like you would with say WinForms (using a ClickEvent and calling a function within the Event Function) like:
protected override void OnClick(EventArgs e){
/*** Function to Execute ***/
}
In WPF you attach the Function to a Command and tell the UIElement to execute the Function attached to the Command instead.
I hope this clears things up...
One option that I've found to work is to set this function up as a Behavior.
The Behavior:
public class WindowCloseBehavior : Behavior<Window>
{
public bool Close
{
get { return (bool) GetValue(CloseTriggerProperty); }
set { SetValue(CloseTriggerProperty, value); }
}
public static readonly DependencyProperty CloseTriggerProperty =
DependencyProperty.Register("Close", typeof(bool), typeof(WindowCloseBehavior),
new PropertyMetadata(false, OnCloseTriggerChanged));
private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = d as WindowCloseBehavior;
if (behavior != null)
{
behavior.OnCloseTriggerChanged();
}
}
private void OnCloseTriggerChanged()
{
// when closetrigger is true, close the window
if (this.Close)
{
this.AssociatedObject.Close();
}
}
}
On the XAML Window, you set up a reference to it and bind the Behavior's Close property to a Boolean "Close" property on your ViewModel:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<i:Interaction.Behaviors>
<behavior:WindowCloseBehavior Close="{Binding Close}" />
</i:Interaction.Behaviors>
So, from the View assign an ICommand to change the Close property on the ViewModel which is bound to the Behavior's Close property. When the PropertyChanged event is fired the Behavior fires the OnCloseTriggerChanged event and closes the AssociatedObject... which is the Window.

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.

Resources