I want to bind the DragCompleted event to one of my ViewModel's Command. I tried the following using Blend but it doesn't work:
<Slider x:Name="slider" HorizontalAlignment="Left" Margin="41,147,0,0" VerticalAlignment="Top" Width="412">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Thumb.DragCompleted">
<i:InvokeCommandAction Command="{Binding DragCompletedCommand}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Slider>
But this doesn't work. When I use the normal binding of event to code behind, it works:
<Slider x:Name="slider" Thumb.DragCompleted="slider_DragCompleted" HorizontalAlignment="Left" Margin="41,147,0,0" VerticalAlignment="Top" Width="412"></Slider>
I tried searching but strangely couldn't find answer to this.
You can write an attached property for this which can look like:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace MyTestApplication
{
internal class SliderExtension
{
public static readonly DependencyProperty DragCompletedCommandProperty = DependencyProperty.RegisterAttached(
"DragCompletedCommand",
typeof(ICommand),
typeof(SliderExtension),
new PropertyMetadata(default(ICommand), OnDragCompletedCommandChanged));
private static void OnDragCompletedCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Slider slider = d as Slider;
if (slider == null)
{
return;
}
if (e.NewValue is ICommand)
{
slider.Loaded += SliderOnLoaded;
}
}
private static void SliderOnLoaded(object sender, RoutedEventArgs e)
{
Slider slider = sender as Slider;
if (slider == null)
{
return;
}
slider.Loaded -= SliderOnLoaded;
Track track = slider.Template.FindName("PART_Track", slider) as Track;
if (track == null)
{
return;
}
track.Thumb.DragCompleted += (dragCompletedSender, dragCompletedArgs) =>
{
ICommand command = GetDragCompletedCommand(slider);
command.Execute(null);
};
}
public static void SetDragCompletedCommand(DependencyObject element, ICommand value)
{
element.SetValue(DragCompletedCommandProperty, value);
}
public static ICommand GetDragCompletedCommand(DependencyObject element)
{
return (ICommand)element.GetValue(DragCompletedCommandProperty);
}
}
}
And your Slider-Definition then looks like:
<Slider x:Name="slider" HorizontalAlignment="Left" Margin="41,147,0,0" VerticalAlignment="Top" Width="412"
extensions:SliderExtension.DragCompletedCommand="{Binding SlideCompletedCommand}"/>
extensions is the namespace where your attached property is located.
And in your ViewModel you have an ICommand-Property called SlideCompletedCommand, which can look like:
private ICommand slideCompletedCommand;
public ICommand SlideCompletedCommand
{
get { return slideCompletedCommand ?? (slideCompletedCommand = new RelayCommand(p => SlideCompleted())); }
}
private void SlideCompleted()
{
// Your slide-completed-code here
}
Related
I am trying to implement Attached Behaviors functionality in the MVVM pattern. I have a Calendar control and would like to handle the MouseDoubleClick event. I was doing that using System.Windows.Interactivity and Interaction.Triggers. However, I am also using BlackoutDates in the Calendar and double-clicking on a Blackout Date results in the last valid selected date being passed to the MouseDoubleClick method, not the date clicked on.
So I am now targeting the CalendarDayButton, which will get me the date clicked on, but CDB doesn't have Commands, so I need to use an Attached Behavior. But I'm still not understanding how to get the MouseDoubleClick handler info into the ViewModel. My current code:
View
<Calendar HorizontalAlignment="Left" VerticalAlignment="Top" Margin="20,48,0,0"
SelectedDate="{Binding ReportDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayDateStart="{Binding ReportDateStart, Mode=OneTime}"
DisplayDateEnd="{Binding ReportDateEnd, Mode=OneTime}"
local:AttachedProperties.RegisterBlackoutDates="{Binding NoProdDates, Mode=OneWay}">
<Calendar.CalendarDayButtonStyle>
<Style TargetType="CalendarDayButton">
<Setter Property="local:AttachedBehaviors.IsValidDateSelected"
Value="{Binding ValidDateSelected, Mode=TwoWay}"/>
</Style>
</Calendar.CalendarDayButtonStyle>
</Calendar>
ViewModel
...
private bool validDateSelected;
public bool ValidDateSelected
{
get { return validDateSelected; }
set
{
if (validDateSelected != value)
{
validDateSelected = value;
RaisePropertyChanged("ValidDateSelected");
}
}
}
...
Attached Behaviors class
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace MDOD
{
public class AttachedBehaviors : DependencyObject
{
public static readonly DependencyProperty IsValidDateSelectedProperty =
DependencyProperty.RegisterAttached("IsValidDateSelected", typeof(bool), typeof(AttachedBehaviors),
new UIPropertyMetadata(false, OnIsValidDateSelectedChanged));
public static bool GetIsValidDateSelected(DependencyObject obj)
{
return (bool)obj.GetValue(IsValidDateSelectedProperty);
}
public static void SetIsValidDateSelected(DependencyObject obj, bool value)
{
obj.SetValue(IsValidDateSelectedProperty, value);
}
private static void OnIsValidDateSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CalendarDayButton cdb = d as CalendarDayButton;
if (cdb != null)
{
if ((bool)e.NewValue)
{
cdb.MouseDoubleClick += cdb_MouseDoubleClick;
}
else
{
cdb.MouseDoubleClick -= cdb_MouseDoubleClick;
}
}
}
private static void cdb_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// How do I get this info to the ViewModel?
}
}
}
I'm using MVVM and have the following problem. My TextBox.Text is bound with UpdateSourceTrigger=LostFocus (thats what the user want). I have a Button with a SaveCommand CommandBinding - this works. Now i have a KeyBinding with Strg+S wich also execute the SaveCommand. And here is the problem: when i m in the Textbox and press Strg+s, the changes are not in the viewmodel.
is there any way to get MVVM Commands with KeyBinding and TextBox UpdateSourceTrigger=LostFocus working together?
some code to check out the problem
<Window>
<Window.InputBindings>
<KeyBinding Key="S" Modifiers="Control" Command="{Binding SaveCommand}"></KeyBinding>
</Window.InputBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding MyText1, UpdateSourceTrigger=LostFocus}" Width="100"></TextBox>
<Button Grid.Row="1" Content="_Save" Command="{Binding SaveCommand}" IsDefault="True"></Button>
</Grid>
</Window>
public partial class MainWindow : Window
{
private Viewmodel _data;
public MainWindow()
{
_data = new Viewmodel();
InitializeComponent();
this.DataContext = _data;
}
}
public class Viewmodel : INPCBase
{
private string _myText1;
private Lazy<DelegateCommand> _save;
public Viewmodel()
{
this._save = new Lazy<DelegateCommand>(()=> new DelegateCommand(this.SaveCommandExecute));
}
private void SaveCommandExecute()
{
MessageBox.Show(MyText1);
}
public string MyText1
{
get { return _myText1; }
set { _myText1 = value; this.NotifyPropertyChanged(()=>MyText1);}
}
public ICommand SaveCommand
{
get { return _save.Value; }
}
}
at the moment i came up with the following workaround. within the usercontrol/views where i define my KeyBindings, i also listen to the PreviewKeyDown event and set the focus to the next element when eg. Strg+S is pressed.
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.S && e.KeyboardDevice.Modifiers == ModifierKeys.Control)
{
var fe = Keyboard.FocusedElement as UIElement;
if (fe != null)
{
fe.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
I have the same problem and end up with attached property for TextBox.
public static bool GetCommitOnSave(DependencyObject obj)
{
return (bool)obj.GetValue(CommitOnSaveProperty);
}
public static void SetCommitOnSave(DependencyObject obj, bool value)
{
obj.SetValue(CommitOnSaveProperty, value);
}
public static readonly DependencyProperty CommitOnSaveProperty =
DependencyProperty.RegisterAttached("CommitOnSave", typeof(bool), typeof(Helper), new PropertyMetadata(false, CommitOnSavePropertyChanged));
private static void CommitOnSavePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox textBox)
{
if ((bool)e.NewValue)
{
if ((bool)e.NewValue)
{
textBox.KeyDown += TextBox_KeyDown;
}
else
{
textBox.KeyDown -= TextBox_KeyDown;
}
}
}
}
private static void TextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
var textBox = (TextBox)sender;
if (e.Key == Key.S && Keyboard.Modifiers == ModifierKeys.Control)
{
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
}
}
Using <TextBox Text="{Binding Name}" local:Helper.CommitOnSave="True" />
Of course you can set attached property in style for all TextBoxes in a form.
I think I find the best solution for me. I mix solution #blindmeis and my previous one with using attached property.
I create command which update binding source of actual keyboard focused element:
public class CommitValueCommand : ICommand
{
private static CommitValueCommand _instance;
public static CommitValueCommand Command => _instance ?? (_instance = new CommitValueCommand());
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (Keyboard.FocusedElement is TextBox textBox)
{
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
}
//for combobox etc.
else if (Keyboard.FocusedElement is Selector selector)
{
BindingOperations.GetBindingExpression(selector, Selector.SelectedValueProperty).UpdateSource();
}
}
}
In Execute method of command SaveCommand just at beginning invoke CommitValueCommand.Command.Execute().
I'm trying to trigger Slider Thumb.DragStarted event by using MVVMLight EventToCommand but it is not working. The same thing is working perfectly for Slider Event ValueChanged.
Below is my code:
<Slider
Width="150"
AutoToolTipPlacement="BottomRight"
AutoToolTipPrecision="2"
IsSnapToTickEnabled="True"
Maximum="{Binding SilderMaxValue}"
Minimum="0"
Value="{Binding SliderValue}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<cmd:EventToCommand
Command="{Binding SliderValueChangedCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="Thumb.DragStarted">
<cmd:EventToCommand
Command="{Binding SliderDragStartedCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</Slider>
Thanks..
I saw your post while I was trying to do something similar (albeit with Thumb.DragCompleted). In any case, I used an attached property. I'll post my solution in case it's of use to anyone.
SliderDragBehavoirs.cs:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfApplication1
{
public static class SliderDragBehaviors
{
public static readonly DependencyProperty DragCompletedCommandProperty =
DependencyProperty.RegisterAttached("DragCompletedCommand", typeof(ICommand), typeof(SliderDragBehaviors),
new FrameworkPropertyMetadata(new PropertyChangedCallback(DragCompleted)));
private static void DragCompleted(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var slider = (Slider)d;
var thumb = GetThumbFromSlider(slider);
thumb.DragCompleted += thumb_DragCompleted;
}
private static void thumb_DragCompleted(object sender, DragCompletedEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
element.Dispatcher.Invoke(() =>
{
var command = GetDragCompletedCommand(element);
var slider = FindParentControl<Slider>(element) as Slider;
command.Execute(slider.Value);
});
}
public static void SetDragCompletedCommand(UIElement element, ICommand value)
{
element.SetValue(DragCompletedCommandProperty, value);
}
public static ICommand GetDragCompletedCommand(FrameworkElement element)
{
var slider = FindParentControl<Slider>(element);
return (ICommand)slider.GetValue(DragCompletedCommandProperty);
}
private static Thumb GetThumbFromSlider(Slider slider)
{
var track = slider.Template.FindName("PART_Track", slider) as Track;
return track == null ? null : track.Thumb;
}
private static DependencyObject FindParentControl<T>(DependencyObject control)
{
var parent = VisualTreeHelper.GetParent(control);
while (parent != null && !(parent is T))
{
parent = VisualTreeHelper.GetParent(parent);
}
return parent;
}
}
}
There's a couple of things worth noting here. Because the command is hooked up to the Slider, but the event is fired on the Thumb, it is necessary to be able to look up/down the visual tree in order to get one from the other.
Example XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behaviors="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="grid">
<Slider behaviors:SliderDragBehaviors.DragCompletedCommand="{Binding Path=DragCompletedCommand}"/>
</Grid>
</Window>
Hope that's of some use :)
I had a problem with the code from Tom Allen because the slider template was not available at the time I wanted to bind it with command. Basically, all i needed to do is wait for the slider control to load and try again.
Here are the changes that i needed to make in order for it to work:
private static void DragCompleted(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//the Template of the slider is not available now
//we have to wait for the slider to load completely in order to do this
var slider = (Slider)d;
slider.Loaded += slider_Loaded;
}
static void slider_Loaded(object sender, RoutedEventArgs e)
{
var slider = (Slider)sender;
var thumb = GetThumbFromSlider(slider);
thumb.DragCompleted += thumb_DragCompleted;
}
Hope it helps!
Regards
I want to invoke a command when ENTER is pressed in a TextBox. Consider the following XAML:
<UserControl
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
...>
...
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction Command="{Binding MyCommand}"
CommandParameter="{Binding Text}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
...
</UserControl>
and that MyCommand is as follows:
public ICommand MyCommand {
get { return new DelegateCommand<string>(MyCommandExecute); }
}
private void MyCommandExecute(string s) { ... }
With the above, my command is invoked for every key press. How can I restrict the command to only invoke when the ENTER key is pressed?
I understand that with Expression Blend I can use Conditions but those seem to be restricted to elements and can't consider event arguments.
I have also come across SLEX which offers its own InvokeCommandAction implementation that is built on top of the Systems.Windows.Interactivity implementation and can do what I need. Another consideration is to write my own trigger, but I'm hoping there's a way to do it without using external toolkits.
There is KeyTrigger in expression blend.
<UserControl
xmlns:i="clr-namespace:System.Windows.Interactivity;
assembly=System.Windows.Interactivity"
xmlns:iex="clr-namespace:Microsoft.Expression.Interactivity.Input;
assembly=Microsoft.Expression.Interactions" ...>
<TextBox>
<i:Interaction.Triggers>
<iex:KeyTrigger Key="Enter">
<i:InvokeCommandAction Command="{Binding PasswordLoginCommand}" />
</iex:KeyTrigger>
</i:Interaction.Triggers>
</TextBox>
</UserControl>
System.Windows.Interactivity and Microsoft.Expression.Interactions assemblies are available for WPF in the official Nuget package.
I like scottrudy's approach (to which I've given a +1) with the custom triggers approach as it stays true to my initial approach. I'm including a modified version of it below to use dependency properties instead of reflection info so that it's possible to bind directly to the ICommand. I'm also including an approach using attached properties to avoid using System.Windows.Interactivity if desired. The caveat to the latter approach is that you lose the feature of multiple invokations from an event, but you can apply it more generally.
Custom Triggers Approach
ExecuteCommandAction.cs
public class ExecuteCommandAction : TriggerAction<DependencyObject> {
#region Properties
public ICommand Command {
get { return (ICommand)base.GetValue(CommandProperty); }
set { base.SetValue(CommandProperty, value); }
}
public static ICommand GetCommand(DependencyObject obj) {
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value) {
obj.SetValue(CommandProperty, value);
}
// We use a DependencyProperty so we can bind commands directly rather
// than have to use reflection info to find them
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(ExecuteCommandAction), null);
#endregion Properties
protected override void Invoke(object parameter) {
ICommand command = Command ?? GetCommand(AssociatedObject);
if (command != null && command.CanExecute(parameter)) {
command.Execute(parameter);
}
}
}
TextBoxEnterKeyTrigger.cs
public class TextBoxEnterKeyTrigger : TriggerBase<UIElement> {
protected override void OnAttached() {
base.OnAttached();
TextBox textBox = this.AssociatedObject as TextBox;
if (textBox != null) {
this.AssociatedObject.KeyUp += new System.Windows.Input.KeyEventHandler(AssociatedObject_KeyUp);
}
else {
throw new InvalidOperationException("This behavior only works with TextBoxes");
}
}
protected override void OnDetaching() {
base.OnDetaching();
AssociatedObject.KeyUp -= new KeyEventHandler(AssociatedObject_KeyUp);
}
private void AssociatedObject_KeyUp(object sender, KeyEventArgs e) {
if (e.Key == Key.Enter) {
TextBox textBox = AssociatedObject as TextBox;
//This checks for an mvvm style binding and updates the source before invoking the actions.
BindingExpression expression = textBox.GetBindingExpression(TextBox.TextProperty);
if (expression != null)
expression.UpdateSource();
InvokeActions(textBox.Text);
}
}
}
MyUserControl.xaml
<UserControl
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:b="clr-namespace:MyNameSpace.Interactivity"
...
<TextBox>
<i:Interaction.Triggers>
<b:TextBoxEnterKeyTrigger>
<b:ExecuteCommandAction Command="{Binding MyCommand}" />
</b:TextBoxEnterKeyTrigger>
</i:Interaction.Triggers>
</TextBox>
...
</UserControl>
Attached Properties Approach
EnterKeyDown.cs
public sealed class EnterKeyDown {
#region Properties
#region Command
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(EnterKeyDown),
new PropertyMetadata(null, OnCommandChanged));
#endregion Command
#region CommandArgument
public static object GetCommandArgument(DependencyObject obj) {
return (object)obj.GetValue(CommandArgumentProperty);
}
public static void SetCommandArgument(DependencyObject obj, object value) {
obj.SetValue(CommandArgumentProperty, value);
}
public static readonly DependencyProperty CommandArgumentProperty =
DependencyProperty.RegisterAttached("CommandArgument", typeof(object), typeof(EnterKeyDown),
new PropertyMetadata(null, OnCommandArgumentChanged));
#endregion CommandArgument
#region HasCommandArgument
private static bool GetHasCommandArgument(DependencyObject obj) {
return (bool)obj.GetValue(HasCommandArgumentProperty);
}
private static void SetHasCommandArgument(DependencyObject obj, bool value) {
obj.SetValue(HasCommandArgumentProperty, value);
}
private static readonly DependencyProperty HasCommandArgumentProperty =
DependencyProperty.RegisterAttached("HasCommandArgument", typeof(bool), typeof(EnterKeyDown),
new PropertyMetadata(false));
#endregion HasCommandArgument
#endregion Propreties
#region Event Handling
private static void OnCommandArgumentChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
SetHasCommandArgument(o, true);
}
private static void OnCommandChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
FrameworkElement element = o as FrameworkElement;
if (element != null) {
if (e.NewValue == null) {
element.KeyDown -= new KeyEventHandler(FrameworkElement_KeyDown);
}
else if (e.OldValue == null) {
element.KeyDown += new KeyEventHandler(FrameworkElement_KeyDown);
}
}
}
private static void FrameworkElement_KeyDown(object sender, KeyEventArgs e) {
if (e.Key == Key.Enter) {
DependencyObject o = sender as DependencyObject;
ICommand command = GetCommand(sender as DependencyObject);
FrameworkElement element = e.OriginalSource as FrameworkElement;
if (element != null) {
// If the command argument has been explicitly set (even to NULL)
if (GetHasCommandArgument(o)) {
object commandArgument = GetCommandArgument(o);
// Execute the command
if (command.CanExecute(commandArgument)) {
command.Execute(commandArgument);
}
}
else if (command.CanExecute(element.DataContext)) {
command.Execute(element.DataContext);
}
}
}
}
#endregion
}
MyUserControl.xaml
<UserControl
...
xmlns:b="clr-namespace:MyNameSpace.Interactivity"
...
<TextBox b:EnterKeyDown.Command="{Binding AddNewDetailCommand}"
b:EnterKeyDown.CommandArgument="{Binding Path=Text,RelativeSource={RelativeSource Self}}" />
...
</UserControl>
I ran into this same issue yesterday and solved it using custom triggers. It may seem a bit much at first, but I found this general pattern is usable for doing a lot of the things that I used to accomplish using event handlers directly in a view (like double click events). The first step is to create a trigger action that can accept a parameter since we will need it later.
public class ExecuteCommandAction : TriggerAction<FrameworkElement>
{
public string Command { get; set; }
protected override void Invoke(object o)
{
if (Command != null)
{
object ctx = AssociatedObject.DataContext;
if (ctx != null)
{
var cmd = ctx.GetType().GetProperty(Command)
.GetValue(ctx, null) as ICommand;
if (cmd != null && cmd.CanExecute(o))
{
cmd.Execute(o);
}
}
}
}
}
The next step is to create the trigger. You could do some interesting things with base classes to make it more generic for capturing different types of key presses, but we'll keep it simple.
public class TextBoxEnterKeyTrigger: TriggerBase<UIElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.KeyUp += AssociatedObject_KeyUp;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.KeyUp -= AssociatedObject_KeyUp;
}
void AssociatedObject_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
TextBox textBox = AssociatedObject as TextBox;
object o = textBox == null ? null : textBox.Text;
if (o != null)
{
InvokeActions(o);
}
}
}
}
Keep in mind that even though you may have a data binding in place to your TextBox value, the property changed event won't fire because your textbox hasn't lost focus. For this reason I am passing the value of the TextBox.Text property to the command. The last step is to use this feature in your XAML. You need to be sure to include the Interactivity namespace as well as the namespace that contains your code from above.
<UserControl
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:common="clr-namespace:My.UI;assembly=My.UI">
<TextBox Text="{Binding Path=MyText, Mode=TwoWay}" IsEnabled="{Binding CanMyCommand}">
<i:Interaction.Triggers>
<common:TextBoxEnterKeyTrigger>
<common:ExecuteCommandAction Command=MyCommand" />
</common:TextBoxEnterKeyTrigger>
</i:Interaction.Triggers>
</TextBox>
</UserControl>
I used scottrudy's code in my application however, my textbox text is bound to some property in viewmodel class and this property is not getting updated by the time command is invoked after pressiong ENTER key because my textbox hasn't lost focus yet. So, to resolved this, i added the following code snippets just above InvokeActions(o) in AssociatedObject_KeyUp method and updated text property is getting updated in viewmodel class.
BindingExpression bindingExpression = (textBox).GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();
On top of my mind.. You can pass event args to command and than in ViewModel check if e.KeyPress = Keys.Enter.. this is not really code :) i dont have my VS on this computer.. this is rather an idea :)
I'm trying to better understand Silverlights binding mechanism and so have created a simple program that will change the borderthickness of a listbox on the press of a button. However it doesn't work and I can't figure out what I am doing wrong. Any ideas?
XAML:
<Grid x:Name="LayoutRoot" Background="White">
<ListBox Height="100" HorizontalAlignment="Left" Margin="134,102,0,0" Name="listBox1" VerticalAlignment="Top" Width="120" BorderThickness="{Binding TheThickness, Mode=TwoWay}" />
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="276,36,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
Code:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace SilverlightApplication4
{
public partial class MainPage : UserControl
{
private TestClass testInst = new TestClass();
public MainPage()
{
InitializeComponent();
listBox1.DataContext = testInst;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
testInst.TheThickness = 10;
}
}
public class TestClass
{
private int theThickness = 5;
public int TheThickness
{
get { return theThickness; }
set
{
theThickness = value;
NotifyPropertyChanged("TheThickness");
}
}
public event PropertyChangedEventHandler PropertyChanged;
// NotifyPropertyChanged will raise the PropertyChanged event, passing the source property that is being updated.
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
A border thickness is of Type Thickness which has multiple values for Top, Bottom, Left and Right. The XAML parser knows how to handle something like BorderThickness="5" correctly but in code you need to use the Thickness type. For example:-
public Thickness SelectedThickness
{
get { return (Thickness)GetValue(SelectedThicknessProperty); }
set { SetValue(SelectedThicknessProperty, value); }
}
public static readonly DependencyProperty SelectedThicknessProperty =
DependencyProperty.Register("SelectedThickness", typeof(Thickness), typeof(MyRectangle),
new PropertyMetadata(new Thickness() { Top = 1, Bottom = 1, Left = 1, Right = 1 }));
}
In this case the default Thickness of 1.
Edit Code more like yours:-
private Thickness theThickness = new Thickness() {Left = 5, Right = 5, Top = 5, Bottom = 5};
public Thickness TheThickness
{
get { return theThickness; }
set
{
theThickness = value;
NotifyPropertyChanged("TheThickness");
}
}