All
I have a issue. Now I'm using MVVM framework to develop Windows Phone 8 app. I just want to when I press the button, then begin to record something, when release the button, stop recording, I used InvokeCommandAction to bind the command in ViewModel, this is the code as follow
Xaml:
<Button x:Name="BtnRecord" Height="50" Width="180" Background="#D43637" Content="Record" Margin="20,0,0,0" Style="{StaticResource BasicButtonStyle}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding StartRecordCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding EndRecordCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
ModelView:
public ICommand StartRecordCommand
{
get
{
return new DelegateCommand(StartRecord);
}
}
public ICommand EndRecordCommand
{
get
{
return new DelegateCommand(EndRecord);
}
}
private void StartRecord(object parameter){}
private void EndRecord(object parameter){}
When I debug the app, I found it didn't fire the neither the MouseLeftButtonDown nor MouseLeftButtonUp events, so I register the two event handler as follow:
BtnRecord.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(Button_MouseLeftButtonDown), true);
BtnRecord.AddHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(Button_MouseLeftButtonUp), true);
private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
}
private void Button_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
}
OK, keep on going, but the next problem is coming, it didn't fire the ICommand in ViewModel, it called the Button_MouseLeftButtonDown, oh, god, I crazy
Anyone know how to call the ICommand in ViewModel?
Or another way to implement it?
You can use ICommand.Execute. So, your handler should be
private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
StartRecordCommand.Execute(null);
}
Try binding the IsPressed property of the Button with a TwoWay binding to a bool property called IsRecording in your ViewModel and start/stop your recording logic from inside the setter based on the new bool value (true would mean start).
Let me know if it works.
Related
In my ViewModel, how can I detect what key was pressed when entering text in a textBox?
In plain WPF/C# I'm doing it like this...
XAML File
<TextBox x:Name="myInputField" KeyDown="inputField_KeyDown"/>
Codebehind .xaml.cs
private void inputField_KeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.IsKeyDown(Key.Enter)) {
// do something
}
}
EDIT:
FYI -What I'm trying to do is create a shortcut for the enter key.
There are a couple of ways to go about this. The first approach would be more MVVM appropriate where we just detect a change to the value of the Text that is bound to your TextBox:
In XAML:
<TextBox x:Name="myInputField",
Text="{Binding MyText, UpdateSourceTrigger=PropertyChanged}" />
In VM
private string myText;
public string MyText
{
get
{
return myText;
}
set
{
if (Set(nameof (MyText), ref myText, value))
{
// the value of the text box changed.. do something here?
}
}
}
Or, to more directly answer the question you asked, if you must rely on detecting a keypress in the textbox, you should take advantage of the EventToCommand that you can hook in with MVVMLight
In XAML:
xmlns:cmd="http://www.galasoft.ch/mvvmlight"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
...
<TextBox ....
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cmd:EventToCommand Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.KeyDownCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Edit
In addition, you could also bind to the KeyBinding Command on the textbox:
<TextBox AcceptsReturn="False">
<TextBox.InputBindings>
<KeyBinding
Key="Enter"
Command="{Binding SearchCommand}"
CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
And yet another option would be to keep handling the KeyDown event in your view, but in the codebehind call a ViewModel method:
As I understand, you actually do not want to send the Key to ViewModel. You just want to trigger something inside your ViewModel.
EventAggregator might be your solution, in your KeyDown event, you trigger event inside VM without knowing VM and you pass anything you want, there are several ways to do it.
If you are using framework like MVVMLight, Prism they might have own implementations, if you don't, there is a simple tutorial for it. (This is not the only way, you can find different implementations when you search observer pattern)
Inside your if you call Publish method which comes from EventAggregator. And all your Subscribers get that with a parameter you choose.
This way you can communicate with your ViewModel from wherever you want.
Personally I have created a Behavior as follows:
public class KeyUpToCommandBehaviour : Behavior<UIElement>
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(KeyUpToCommandBehaviour), new PropertyMetadata(default(ICommand)));
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty KeyProperty = DependencyProperty.Register(
"Key", typeof(Key), typeof(KeyUpToCommandBehaviour), new PropertyMetadata(default(Key)));
private RoutedEventHandler _routedEventHandler;
public Key Key
{
get { return (Key) GetValue(KeyProperty); }
set { SetValue(KeyProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
_routedEventHandler = AssociatedObject_KeyUp;
AssociatedObject.AddHandler(UIElement.KeyUpEvent, _routedEventHandler, true);
}
void AssociatedObject_KeyUp(object sender, RoutedEventArgs e)
{
var keyArgs = e as KeyEventArgs;
if (keyArgs == null)
return;
if(keyArgs.Key == Key)
Command?.Execute(null);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.RemoveHandler(UIElement.KeyUpEvent, _routedEventHandler);
}
}
And then use it as
<TextBox ....
<i:Interaction.Behaviors>
<attachedBehaviors:KeyUpToCommandBehaviour Key="Enter" Command="{Binding OpenFxTradeTargetingWizardCommand}"/>
</i:Interaction.Behaviors>
</TextBox>
I have implemented MVVM pattern in my WPF windows. I have a subwindow which I call the following way (from another ViewModel):
cmd_Show = new DelegateCommand(
(sender) =>
{
frm_Strediska _window = new frm_Strediska();
frm_StrediskaViewModel vm = new frm_StrediskaViewModel(ZakladneStrediska, _window);
_window.DataContext = vm;
_window.Owner = App.Current.MainWindow;
_window.ShowDialog();
});
Here under frm_StrediskaViewModel I hook to a Window.Closing event in a following way:
public frm_StrediskaViewModel(ObservableCollection<DefaultStrediska> _Strediska, frm_Strediska _Window)
{
Window = _Window;
Strediska = _Strediska;
InitializeCommands();
Window.Closing += Window_Closing;
}
The thing I am not sure about is, that when I close this SubWindow, if my Window.Closing event unhooks automatically, or I have to override Dispose() event on the ViewModel (that's what I am doing now):
protected override void Dispose(bool disposing)
{
Window.Closing -= Window_Closing;
this.Dispose();
}
Is this a good approach, or is it completely unnecessary?
There is a way to do this with neat bindings, but it requires the Blend SDK. You need the System.Windows.Interactivity.dll for this. The first thing required is to add the namespace declaration:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
With this import you gain the possibility to bind commands to events like so:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<i:InvokeCommandAction Command="{Binding CloseCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Adding this to your window will cause the command bound with CloseCommand to be executed once the Closing-Event is raised by the window.
So you don't need to do any binding to the event yourself. You can just create the required property in your ViewModel that returns the command. This command has to do what ever is required during the closing. So basically what you got in your event handler currently.
public readonly ICommand CloseCommand
{
get { return /* Your closing command here */; }
}
I hope that helps.
I have a button
<Button Command="{Binding MyCommand}" />
But MyCommand is being hit when I long-press the button as well as when I just click it. Is there any way around this?
thanks
What you could do is the following:
Reference the System.Windows.Interactivity dll.
Define the namespace in your xaml code:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Then in your xaml code wire up this event trigger:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap" SourceName="btnTest">
<i:InvokeCommandAction Command="{Binding DoSomething}" />
</i:EventTrigger>
</i:Interaction.Triggers>
The EventName in this case is "Tap", the SourceName is the x:Name of the button you want to watch. Like this:
<Button Content="Click me" x:Name="btnTest"/>
Then in your ViewModel, you can wire it up to an ICommand, I typically use a RelayCommand:
private ICommand _DoSomething;
public ICommand DoSomething
{
get
{
if (_DoSomething == null)
{
_DoSomething = new RelayCommand(DoSomethingExecute);
}
return _DoSomething;
}
}
private void DoSomethingExecute()
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show("btnTest on the tap event");
});
}
I tested, only the tap event is captured, not the long press event.
I'm working on this (MVVM) Windows 8.1 metro app where I'm using a UserControl that I built separately, this UserControl has its own events that I built for it.
My problem is: when I try in the global app to use commands on those they never fires, although the events work!
The UserControl and its events :
public delegate void PlayClickedEventHandler(object sender, RoutedEventArgs e);
...
public event PlayClickedEventHandler PlayClicked;
The PlayClicked event is fired when a button Inside the UserControl is clicked
private void PlayButton_OnClick(object sender, RoutedEventArgs e)
{
if (PlayClicked != null)
PlayClicked(this, e);
}
The Global Windows 8.1 MVVM App :
The View :
<mediaPlayerControl:PlayerControl x:Name="UserControl">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="PlayClicked">
<core:InvokeCommandAction Command="{Binding OnPlayClicked, Mode=TwoWay}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</mediaPlayerControl:PlayerControl>
The ViewModel :
public RelayCommand OnPlayClicked { get; set; }
OnPlayClicked = new RelayCommand(() =>
{
//Stuff to do here that seem not to be done
});
Am I missing something ??
I guess this could explain it (from the docs on EventTriggerBehavior)
The following events are supported:
(list of 11 events)
Consider implementing a custom behavior to respond to other events.
Why not use an EventTrigger instead? (Also, note that the "OnPlayClicked" binding should really be one way, although this shouldn't make a difference.)
<mediaPlayerControl:PlayerControl x:Name="UserControl">
<interactivity:Interaction.Triggers>
<uixaml:EventTrigger EventName="PlayClicked">
<core:InvokeCommandAction Command="{Binding OnPlayClicked}"/>
</uixaml:EventTrigger>
</interactivity:Interaction.Triggers>
</mediaPlayerControl:PlayerControl>
I've a Text box KeyUp Event Trigger Wired up to a command in WPF.
I need to pass the actual key that was pressed as a command parameter.
The command executes fine, but the code that handles it needs to know the actual key that was pressed (remember this could be an enter key or anything not just a letter, so I can't get it from the TextBox.text).
Can't figure out how to do this.
XAML:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
XAML:
<TextBox Height="23" Name="TextBoxSelectionSearch" Width="148" Tag="Enter Selection Name" Text="{Binding Path=SelectionEditorFilter.SelectionNameFilter,UpdateSourceTrigger=PropertyChanged}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction Command="{Binding SelectionEditorSelectionNameFilterKeyUpCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
I don't think that's possible with InvokeCommandAction but you can quickly create your own Behavior which could roughly look like this one:
public class KeyUpWithArgsBehavior : Behavior<UIElement>
{
public ICommand KeyUpCommand
{
get { return (ICommand)GetValue(KeyUpCommandProperty); }
set { SetValue(KeyUpCommandProperty, value); }
}
public static readonly DependencyProperty KeyUpCommandProperty =
DependencyProperty.Register("KeyUpCommand", typeof(ICommand), typeof(KeyUpWithArgsBehavior), new UIPropertyMetadata(null));
protected override void OnAttached()
{
AssociatedObject.KeyUp += new KeyEventHandler(AssociatedObjectKeyUp);
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.KeyUp -= new KeyEventHandler(AssociatedObjectKeyUp);
base.OnDetaching();
}
private void AssociatedObjectKeyUp(object sender, KeyEventArgs e)
{
if (KeyUpCommand != null)
{
KeyUpCommand.Execute(e.Key);
}
}
}
and then just attach it to the TextBox:
<TextBox Height="23" Name="TextBoxSelectionSearch" Width="148" Tag="Enter Selection Name" Text="{Binding Path=SelectionEditorFilter.SelectionNameFilter,UpdateSourceTrigger=PropertyChanged}" >
<i:Interaction.Behaviors>
<someNamespace:KeyUpWithArgsBehavior
KeyUpCommand="{Binding SelectionEditorSelectionNameFilterKeyUpCommand}" />
</i:Interaction.Behaviors>
</TextBox>
With just that you should receive the Key as a parameter to the command.