I'm new to WPF. Currently, I want to allow my Add button to add item by using either single click or double click. However, when I try to double click, it ends up fire single click event twice. Code in XAML as below:
<Button.InputBindings>
<MouseBinding Command="{Binding Path=AddCommand}" CommandParameter="{Binding}" MouseAction="LeftClick" />
<MouseBinding Command="{Binding Path=AddCommand}" CommandParameter="{Binding}" MouseAction="LeftDoubleClick" />
I found solution online which is to use DispatcherTimer in order to solve the problem. I have inserted these in code behind:
private static DispatcherTimer myClickWaitTimer =
new DispatcherTimer(
new TimeSpan(0, 0, 0, 1),
DispatcherPriority.Background,
mouseWaitTimer_Tick,
Dispatcher.CurrentDispatcher);
private void btnAdd_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// Stop the timer from ticking.
myClickWaitTimer.Stop();
// Handle Double Click Actions
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
myClickWaitTimer.Start();
}
private static void mouseWaitTimer_Tick(object sender, EventArgs e)
{
myClickWaitTimer.Stop();
// Handle Single Click Actions
}
So here comes my question. I've removed the MouseBinding in XAML and want to call for AddCommand in code behind but I'm having problem to do so due to the PrismEventAggregator. The AddCommand in .cs as below:
private void AddCommandExecute(Object commandArg)
{
// Broadcast Prism event for adding item
this.PrismEventAggregator.GetEvent<AddItemEvent>().Publish(
new AddItemPayload()
{
BlockType = this.BlockType
}
);
}
Hence would like to know how to call for the AddCommand (which is a Prism Event in .cs) in Code behind?
Note: The button is inside resource dictionary thus I failed to use the button name to call for the command.
You need to create a class which will subscribe to the event you are publishing and then execute the logic you want.
For example:
public class AddItemViewModel : INotifyPropertyChanged
{
private IEventAggregator _eventAggregator;
public AddItemViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
_eventAggregator.GetEvent<AddItemEvent>().Subscribe(AddItem);
}
private void AddItem(AddItemPayload payload)
{
// Your logic here
}
}
Then when you publish the event it will trigger the subscriber and execute.
Using Expression Blend SDK, you can create a Behavior that encapsulates all your custom logic. This behavior will offer two dependency properties for your command and its parameter, so you can easily create Bindings for them, exactly as you do this for your InputBindings.
Move your event handlers and DispatcherTimer logic into this behavior:
using System.Windows.Interactivity;
class ClickBehavior : Behavior<Button>
{
// a dependency property for the command
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand),
typeof(ClickBehavior), new PropertyMetadata(null));
// a dependency property for the command's parameter
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object),
typeof(ClickBehavior), new PropertyMetadata(null));
public ICommand Command
{
get { return (ICommand)this.GetValue(CommandProperty); }
set { this.SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return this.GetValue(CommandParameterProperty); }
set { this.SetValue(CommandParameterProperty, value); }
}
// on attaching to a button, subscribe to its Click and MouseDoubleClick events
protected override void OnAttached()
{
this.AssociatedObject.Click += this.AssociatedObject_Click;
this.AssociatedObject.MouseDoubleClick += this.AssociatedObject_MouseDoubleClick;
}
// on detaching, unsubscribe to prevent memory leaks
protected override void OnDetaching()
{
this.AssociatedObject.Click -= this.AssociatedObject_Click;
this.AssociatedObject.MouseDoubleClick -= this.AssociatedObject_MouseDoubleClick;
}
// move your event handlers here
private void AssociatedObject_Click(object sender, RoutedEventArgs e)
{ //... }
private void AssociatedObject_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{ //... }
// call this method in your event handlers to execute the command
private void ExecuteCommand()
{
if (this.Command != null && this.Command.CanExecute(this.CommandParameter))
{
this.Command.Execute(this.CommandParameter);
}
}
The usage is very simple. You need to declare your additional namespaces:
<Window
xmlns:local="Your.Behavior.Namespace"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
...
Finally, attach the behavior to the button:
<Button>
<i:Interaction.Behaviors>
<local:ClickBehavior Command="{Binding AddCommand}" CommandParameter="{Binding}"/>
</i:Interaction.Behaviors>
</Button>
Related
I'm working on a custom WPF UserControl and having an issue with one of my DependencyProperties.
So I built a test scenario that looks like this. In the Custom Control..
public static readonly DependencyProperty MyCollectionItemsSourceProperty = DependencyProperty.Register("DynamicHeaderItemsSource", typeof(IEnumerable), typeof(TestUserControl1),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnMyCollectionItemsSourceChanged)));
public IEnumerable MyCollectionItemsSource
{
get { return (IEnumerable)GetValue(MyCollectionItemsSourceProperty ); }
set { SetValue(MyCollectionItemsSourceProperty , value); }
}
protected static void OnMyCollectionItemsSourceChanged(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
System.Diagnostics.Debug.WriteLine("MyCollection Updated");
}
In my test window's code behind:
public ObservableCollection<string> MyTestStrings { get; set; }
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MyTestStrings.Add("First");
MyTestStrings.Add("Second");
MyTestStrings.Add("Third");
}
And in my test window's XAML:
<Grid>
<local:TestUserControl1 MyCollectionItemsSource="{Binding MyTestStrings}">
</Grid>
The problem is, I never get a notification of any type when the underline collection changes. The OnMyCollectionItemsSourceChanged only ever gets called once: at the beginning when the binding is set. What am I missing?
It is an expected behavior your MyCollectionItemsSource just change when it is set in XAML binding since (one time )t hen those adds in the collection is not changing your property itself (it is doing something inside of the collection).
if you want to get information about changing collection you have to first in OnMyCollectionItemsSourceChanged event test if the vale supports INotifyCollectionChanged this then register for NotifyCollectionChangedEventHandler isndie, do not forget to unregister your handler
protected static void OnMyCollectionItemsSourceChanged(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
if( args.OldValue is INotifyCollectionChanged)
(args.OldValue as INotifyCollectionChanged ).CollectionChanged -= CollectionChangedHandler;
if(args.NewValue is INotifyCollectionChanged)
(args.OldValue as INotifyCollectionChanged).CollectionChanged += CollectionChangedHandler;
}
private static void CollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
{
//
}
The PropertyChangedCallback will be called only when the property is set (or nullified) not if there are any changes to the collection itself (adding/removing elements). To do that you will have to hook up to the CollectionChanged event. See this post: https://stackoverflow.com/a/12746855/4173996
I have a view model that provides a RelayCommand LoadImage.
Typically I would use a button and bind the command to this button.
However I would like to call the LoadImage command from view's codebehind (I need to do some view related stuff that must not be put into view model)
The one way I am aware is to create an event handler for the button, e.g. Button_Click.
In Button_Click I would cast DataContext to the corresponding ViewModel and use this instance to call (DataContext as MyViewModel).LoadImage.Execute(...)
This is odd as I need to know the view model.
What I am trying, is to bind LoadImage not to a button but to a resource in the view, so the Button_Click event just need to call FindResource with a given name and cast it to ICommand without the necessity to know the specific ViewModel.
Is this possible? The command itself is not static as it needs to know the context in what it is called.
You can make it by creating a behavior, which requires Prism referred in your project:
public class LoadImageBehavior : Behavior<Button>
{
public public static static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof (ICommand), typeof (LoadImageBehavior));
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += AssociatedObject_Click;
}
private void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
//Logic...
if(Command != null && Command.CanExecute(null))
Command.Execute(null);
//Logic...
}
}
On Xaml:
<Button>
<i:Interaction.Behaviors>
<Behaviors:LoadImageBehavior Command="{Binding LoadImageCommand}"/>
</i:Interaction.Behaviors>
</Button>
Based on Bill Zhangs idea of behaviours I've created a generic version which is quite control agnostic and which allows to be reused.
The required assembly is
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
I've created a Trigger action that passes the execution along to an event handler:
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
using System;
namespace Misc
{
public class CommandWithEventAction : TriggerAction<UIElement>
{
public event Func<object, object> Execute;
public static DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandWithEventAction), null);
public ICommand Command
{
get
{
return (ICommand)GetValue(CommandProperty);
}
set
{
SetValue(CommandProperty, value);
}
}
public static DependencyProperty ParameterProperty = DependencyProperty.Register("Parameter", typeof(object), typeof(CommandWithEventAction), null);
public object Parameter
{
get
{
return GetValue(ParameterProperty);
}
set
{
SetValue(ParameterProperty, value);
}
}
protected override void Invoke(object parameter)
{
var result = Execute(Parameter);
Command.Execute(result);
}
}
}
To avoid any logic in a custom behaviour this allows to hook up any event to an event callback followed by a command call.
XAML:
<Button>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<misc:CommandWithEventAction Command="{Binding LoadImageCommand}" Parameter="Custom data" Execute="CommandWithEventAction_OnExecute"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Execute
</Button>
This will pass the "Custom data" string boxed as object to a function called
CommandWithEventAction_OnExecute
its signature of Func<object,object> may use the parameter and need to return something that will then be boxed into object and passed to the LoadImageCommand
I am having a problem with xaml ... a button I have created is not enable. here is the xaml part:
<Button Margin="0,2,2,2" Width="70" Content="Line"
Command="{x:Static local:DrawingCanvas.DrawShape}"
CommandTarget="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Window}}, Path=DrawingTarget}"
CommandParameter="Line">
</Button>
Before Constructor it goes:
public static RoutedCommand DrawShape = new RoutedCommand();
in ctor I have:
this.CommandBindings.Add(new CommandBinding(DrawingCanvas.DrawShape, DrawShape_Executed, DrawShapeCanExecute));
Then I have:
private void DrawShapeCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true; **//Isn't this enough to make it enable?**
en.Handled = true;
}
private void DrawShape_Executed(object sender, ExecutedRoutedEventArgs e)
{
switch (e.Parameter.ToString())
{
case "Line":
//some code here (incomplete yet)
break;
}
When I remove the first line (Command="{x:Static ...}") in the block it gets enable again!
Be sure the CanExecute property of that command is returning true. If it returns false, it automatically disables the control that utilizes that command.
Can execute should return a bool, I'm a little surprised that doesn't give a compile error. Anyway try to change it to this.
private bool DrawShapeCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
return true;
}
EDIT:
Ok since you just revealed all you want is a simple button that executes a command here's a very simple implementation copied from one of my recent projects. First define this class somewhere.
public class GenericCommand : ICommand
{
public event EventHandler CanExecuteChanged { add{} remove{} }
public Predicate<object> CanExecuteFunc{ get; set; }
public Action<object> ExecuteFunc{ get; set; }
public bool CanExecute(object parameter)
{
return CanExecuteFunc(parameter);
}
public void Execute(object parameter)
{
ExecuteFunc(parameter);
}
}
Next define a command in your view model and define both the properties I created in the generic command (it's just the basic stuff that comes along with implementing the ICommand interface).
public GenericCommand MyCommand { get; set; }
MyCommand = new GenericCommand();
MyCommand.CanExecuteFunc = obj => true;
MyCommand.ExecuteFunc = obj => MyMethod;
private void MyMethod(object parameter)
{
//define your command here
}
Then just wire up the button to your command.
<Button Command="{Binding MyCommand}" />
If this is all too much for you (MVVM does require a little extra initial setup). You can always just do this...
<Button Click="MyMethod"/>
private void MyMethod(object sender, RoutedEventArgs e)
{
//define your method
}
I have this markupExtension Class
[MarkupExtensionReturnType(typeof(FrameworkElement))]
[ContentProperty("content")]
public class InsereSom : MarkupExtension
{
public InsereSom()
{ }
[ConstructorArgument("Ligado")]
public bool Ligado
{
get;
set;
}
[ConstructorArgument("Evento")]
public RoutedEvent Evento
{
get;
set;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
FrameworkElement elemento = target.TargetObject as FrameworkElement;
RoutedEventHandler metodo = new RoutedEventHandler(EventoInsereSom);
elemento.AddHandler(Evento, metodo);
EventInfo eventInfo = elemento.GetType().GetEvent("Click");
FrameworkElement parentClass = (MainWindow)((Grid)elemento.Parent).Parent;
Delegate methodDelegate = Delegate.CreateDelegate(eventInfo.EventHandlerType, parentClass, "Button_Click");
eventInfo.RemoveEventHandler(elemento, methodDelegate);
eventInfo.AddEventHandler(elemento, methodDelegate);
return new System.Windows.Controls.Label();
}
public void EventoInsereSom(object sender, RoutedEventArgs e)
{
MessageBox.Show("Hello Extension Markup");
}
And this Xaml
<Button Width="80" Height="25" Click="Button_Click" Name="BtnTeste">
<Cei:InsereSom Ligado="True" Evento="Button.Click"/>
</Button>
And this code behind
public void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Event code behind");
}
I'd like that my method in my markup class execute first than the method in the code behind.
I try to add and remove the EventHandler but for that I need the event name ("Button_Click"). But cant use it hard code.
are there any other way to to id?
Thanks.
I'd like that my method in my markup class execute first than the method in the code behind.
It's not possible, the order in which event handlers are called can only be controlled by the class that raises the event (the button in that case). It's like a newspaper: when you subscribe to it, you can't say "I want to receive my paper before my neighbor"...
However there is a way to have the markup extension detect the click before the code-behind: you can make it handle the PreviewClick event (which is the tunnelling version of Click)
What's a good method to bind Commands to Events? In my WPF app, there are events that I'd like to capture and process by my ViewModel but I'm not sure how. Things like losing focus, mouseover, mousemove, etc. Since I'm trying to adhere to the MVVM pattern, I'm wondering if there's a pure XAML solution.
Thanks!
Use System.Windows.Interactivity
…xmlns:i=http://schemas.microsoft.com/expression/2010/interactivity…
<Slider
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<i:InvokeCommandAction
Command="{Binding MyCommand}"
CommandParameter="{Binding Text, ElementName=textBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Slider>
Make sure your project references the assembly System.Windows.Interactivity.
Source: MSDN Blog Executing a command from an event of your choice
[Update]
Have a look to to Microsoft.Xaml.Behaviors.Wpf (available since 03.12.2018) Official package by Microsoft.
Have a look at Marlon Grech's Attached Command Behaviour, it could be exactly what you're looking for
In order to handle events, you must have some code that attaches itself to the event and executes your command in response. The final goal is to have in XAML:
MouseMoveCommand="{Binding MyCommand}"
In order to achieve this you need to define an attached property for each event that you want to handle. See this for an example and a framework for doing this.
I implemented it using Attached Properties and Reflection. I cannot say it is the best implementation, but I will maybe improve it and it may be a good start for you.
public class EventBinding : DependencyObject
{
public static string GetEventName(DependencyObject obj)
{
return (string)obj.GetValue(EventNameProperty);
}
public static void SetEventName(DependencyObject obj, string value)
{
obj.SetValue(EventNameProperty, value);
var eventInfo = obj.GetType().GetEvent(value);
var eventHandlerType = eventInfo.EventHandlerType;
var eventHandlerMethod = typeof(EventBinding).
GetMethod("EventHandlerMethod", BindingFlags.Static | BindingFlags.NonPublic);
var eventHandlerParameters = eventHandlerType.GetMethod("Invoke").GetParameters();
var eventArgsParameterType = eventHandlerParameters.
Where(p => typeof(EventArgs).IsAssignableFrom(p.ParameterType)).
Single().ParameterType;
eventHandlerMethod = eventHandlerMethod.MakeGenericMethod(eventArgsParameterType);
eventInfo.AddEventHandler(obj, Delegate.CreateDelegate(eventHandlerType, eventHandlerMethod));
}
private static void EventHandlerMethod<TEventArgs>(object sender, TEventArgs e)
where TEventArgs : EventArgs
{
var command = GetCommand(sender as DependencyObject);
command.Execute(new EventInfo<TEventArgs>(sender, e));
}
public static readonly DependencyProperty EventNameProperty =
DependencyProperty.RegisterAttached("EventName", typeof(string), typeof(EventHandler));
public static ICommand GetCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CommandProperty, value);
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(EventBinding));
}
public class EventInfo<TEventArgs>
{
public object Sender { get; set; }
public TEventArgs EventArgs { get; set; }
public EventInfo(object sender, TEventArgs e)
{
Sender = sender;
EventArgs = e;
}
}
public class EventInfo : EventInfo<EventArgs>
{
public EventInfo(object sender, EventArgs e)
: base(sender, e) { }
}
public class EventBindingCommand<TEventArgs> : RelayCommand<EventInfo<TEventArgs>>
where TEventArgs : EventArgs
{
public EventBindingCommand(EventHandler<TEventArgs> handler)
: base(info => handler(info.Sender, info.EventArgs)) { }
}
Examples of usage:
View
<DataGrid local:EventBinding.EventName="CellEditEnding"
local:EventBinding.Command="{Binding CellEditEndingCommand}" />
Model
private EventBindingCommand<DataGridCellEditEndingEventArgs> _cellEditEndingCommand;
public EventBindingCommand<DataGridCellEditEndingEventArgs> CellEditEndingCommand
{
get
{
return _cellEditEndingCommand ?? (
_cellEditEndingCommand = new EventBindingCommand<DataGridCellEditEndingEventArgs>(CellEditEndingHandler));
}
}
public void CellEditEndingHandler(object sender, DataGridCellEditEndingEventArgs e)
{
MessageBox.Show("Test");
}
I don't think you can use it in pure XAML, but take a look at the Delegate Command.
Execute Command, Navigate Frame, and Delegating Command behaviour is a pretty good pattern. It is also can be used in the Expression Blend.
On the "best practices" side, you should think twice before converting an event to a command. Normally, command is something user does intentionaly, an event most often is just an interaction trail, and should not leave the view boundaries.