Disabled button in xaml - c#

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
}

Related

Prevent button command from executing from OnClick when a condition is met

I have a RoutedUI Command that is bound as a Command property for a button and an OnClick event. Whenever I evaluate some condition from the OnClick I want to prevent the command from executing. I referred to this post but dosen't help much Prevent command execution. One quick fix is to get the sender of button on click and set its command to null. But I want to know if there is an other way. Please help.
<Button DockPanel.Dock="Right"
Name="StartRunButtonZ"
VerticalAlignment="Top"
Style="{StaticResource GreenGreyButtonStyle}"
Content="{StaticResource StartARun}"
Width="{StaticResource NormalEmbeddedButtonWidth}"
Click="StartRunButton_Click"
Command="{Binding StartRunCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl},AncestorLevel=2}}"
/>
Here is the code behind
private void StartRunButton_Click(object sender, RoutedEventArgs e)
{
if(SomeCondition){
//Prevent the Command from executing.
}
}
Assuming your StartRun() method follows the async / await pattern, then replace your ICommand implementation with the following. It will set CanExecute to false while the task is running, which will automatically disable the button. You don't need to mix commands and click event handlers.
public class perRelayCommandAsync : ViewModelBase, ICommand
{
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
public perRelayCommandAsync(Func<Task> execute) : this(execute, () => true) { }
public perRelayCommandAsync(Func<Task> execute, Func<bool> canExecute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
private bool _isExecuting;
public bool IsExecuting
{
get => _isExecuting;
set
{
if(Set(nameof(IsExecuting), ref _isExecuting, value))
RaiseCanExecuteChanged();
}
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => !IsExecuting
&& (_canExecute == null || _canExecute());
public async void Execute(object parameter)
{
if (!CanExecute(parameter))
return;
IsExecuting = true;
try
{
await _execute().ConfigureAwait(true);
}
finally
{
IsExecuting = false;
}
}
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
More details at my blog post.
This simple button extension could be useful for someone.
On button click at first it is invoked ConfirmationClick event. If you set in its callback e.IsConfirmed to true, then classic click event is invoked and command is executed.
Because of command binding you have button.IsEnabled property tied up to command.CanExecute.
public class ConfirmationButton : Button
{
public event EventHandler<ConfirmationEventArgs> ConfirmationClick;
protected override void OnClick()
{
ConfirmationEventArgs e = new ConfirmationEventArgs();
ConfirmationClick?.Invoke(this, e);
if (e.IsConfirmed == true)
{
base.OnClick();
}
}
}
public class ConfirmationEventArgs : EventArgs
{
public bool? IsConfirmed = false;
}
<model:ConfirmationButton x:Name="DeleteButton"
ConfirmationClick="DeleteButton_ConfirmationClick"
Command="{Binding DeleteCommand}"/>
private void DeleteButton_ConfirmationClick(object sender, ConfirmationEventArgs e)
{
var dialogWindow = new MyDialogWindow("Title..","Message.."); //example
e.IsConfirmed = dialogWindow.ShowDialog();
}

WPF Binding: disable TextBoxes + Validation and DataContext Issue

My simple program has two windows:
from the first one I set a Boolean value, which then...
I'll use in the second window to disable a number of TextBoxes depending on the aforementioned value itself.
Said TextBoxes are also characterized by a validation binding. At now my validation task is flawlessly working, but I'm not able to make the binding to the IsEnabled TextBox property work.
This is the snippet of my XAML containing one of the TextBoxes (for now the only one I've bound):
<TextBox x:Name="tbSlave1" Validation.Error="ValidationError" IsEnabled="{Binding TextBoxEnabled}" Text="{Binding UpdateSourceTrigger=PropertyChanged, Path=SlavePoint1Name, ValidatesOnDataErrors=true, NotifyOnValidationError=true}"/>
While this is my second window class:
public partial class GeneratorWindow : Window, INotifyPropertyChanged
{
private readonly Validator validator = new Validator();
private int noOfErrorsOnScreen;
public GeneratorWindow()
{
this.InitializeComponent();
this.grid.DataContext = this.validator;
}
public int NumberOfPoints { private get; set; }
public int MainPDC { private get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private Boolean IsEnabled;
public Boolean TextBoxEnabled
{
get { return IsEnabled; }
set
{
IsEnabled = value;
NotifyPropertyChanged("TextBoxEnabled");
}
}
private void ValidationError(object sender, ValidationErrorEventArgs eventArgs)
{
if (eventArgs.Action == ValidationErrorEventAction.Added)
{
this.noOfErrorsOnScreen++;
}
else
{
this.noOfErrorsOnScreen--;
}
}
private void ValidationCanBeExecuted(object sender, CanExecuteRoutedEventArgs eventArgs)
{
eventArgs.CanExecute = this.noOfErrorsOnScreen == 0;
eventArgs.Handled = true;
}
private void ValidationExecuted(object sender, ExecutedRoutedEventArgs eventArgs)
{
// If the validation was successful, let's generate the files.
this.Close();
eventArgs.Handled = true;
}
}
For now, what I'm getting back is that my window is disabled (can't select any TextBox) and, obviously, this:
System.Windows.Data Error: 40 : BindingExpression path error: 'TextBoxEnabled' property not found on 'object' ''Validator' (HashCode=14499481)'. BindingExpression:Path=TextBoxEnabled; DataItem='Validator' (HashCode=14499481); target element is 'TextBox' (Name='tbSlave1'); target property is 'IsEnabled' (type 'Boolean')
From what I can understand, the culprit is the way I'm managing the DataContext in my class constructor. I probably need to add something to the validator line or totally change it, but I can't understand how.
I think you should be fine if you set the DataContext to the GeneratorWindow and updated you bindings accordingly. For that to work you need to change Validator to a public property.
Changed Validator definition:
public Validator Validator { get; } = new Validator();
DataContext:
this.grid.DataContext = this;
Updated Binding:
<TextBox x:Name="tbSlave1"
Validation.Error="ValidationError"
IsEnabled="{Binding TextBoxEnabled}"
Text="{Binding UpdateSourceTrigger=PropertyChanged,
Path=Validator.SlavePoint1Name,
ValidatesOnDataErrors=true,
NotifyOnValidationError=true}"/>

How to call Prism Event in Code Behind

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>

C# how to execute the markup event first?

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)

Binding Commands to Events?

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.

Categories