I'm trying to bind the "DataClick" event of LiveChart's Cartesian Chart element using MVVM pattern.
I have my Charts.xml like this:
<ContentControl Grid.Row="0">
<lvc:CartesianChart x:Name="ContrastChart" Series="{Binding ContrastSeriesCollection}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DataClick">
<i:InvokeCommandAction Command="{Binding ChartDataClick}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</lvc:CartesianChart>
</ContentControl>
This is my ICommand ChartDataClick on my ViewModel:
public ICommand ChartDataClick {
get
{
if(_dataClickCommand == null)
{
_dataClickCommand = new DelegateCommand(
() =>
{
MessageBox.Show("Data Clicked!");
}
);
}
return _dataClickCommand;
}
}
If I switch e.g "DataClick" for "MouseEnter" I get my command fired.
So I'm assuming that the problem is that the DataClick is a custom event.
Anybody knows a workaround for this?
I really tried everything I could find on Google that could help, but nothing so far...
LiveCharts Events: Events Documentation
The EventTrigger doesn't discriminate.
We can check this by implementing MyButtonSimple which has a custom Routed Event Tap.
We can go from handler in code behind
<custom:MyButtonSimple
x:Name="mybtnsimple" Tap="mybtnsimple_Tap"
Content="Click to see Tap custom event work">
</custom:MyButtonSimple>
To a ViewModel ICommand
<custom:MyButtonSimple
x:Name="mybtnsimple"
Content="Click to see Tap custom event work">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding Command}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</custom:MyButtonSimple>
And everything works as expected
The shortcoming of these triggers is that they have to be placed on the UIElement raising the event.
In other words, they ignore Bubbling or Tunneling events. That's why there is no Interaction.Triggers alternative for:
<Grid custom:MyButtonSimple.Tap="mybtnsimple_Tap">
<custom:MyButtonSimple
x:Name="mybtnsimple"
Content="Click to see Tap custom event work">
</custom:MyButtonSimple>
</Grid>
To sum it up, the DataClick event isn't raised on the CartesianChart (but further down the Logical Tree) and therefore you can't handle it this way.
Related
I'm writing WPF app in MVVM using MVVM Light. I have an event trigger in DataGrid to detecting the cell editing ends.
In viewmodel I have command which needs a DataGrid binding item as param. I did it using casting DataGridCellEditEndingEventArgs.EditingElement.DataContext to my model. It's work as I want but it's hard to VM testing.
Here's View's trigger
// xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<DataGrid x:Name="PeopleDataGrid" ItemsSource="{Binding People}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="CellEditEnding">
<cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding EditPersonRowCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
And in VM here's the command
public RelayCommand<DataGridCellEditEndingEventArgs> EditPersonRowCommand
{
get
{
return editPersonRowCommand ??
(editPersonRowCommand =
new RelayCommand<DataGridCellEditEndingEventArgs>(param => this.EditPersonRow(param.EditingElement.DataContext as PersonForListDto), this.editPersonRowCommandCanExecute));
}
}
It's possible to using IValueConverter or something to have model right way without control casting?
The PassEventArgsToCommand dependency property pass the event argument to command. Instead of using PassEventArgsToCommand, you can define the binding for CommandParameter to pass the DataContext. With this, at VM, the RelayCommand can define with actual type. The code at View and ViewModel will be as follows:
<i:Interaction.Triggers>
<i:EventTrigger EventName="CellEditEnding">
<cmd:EventToCommand Command="{Binding EditPersonRowCommand}" CommandParameter="{Binding //Since you have not given the full code so not sure how Binding is cascading so if you require to use ReleativeSource to bind to DataContext then use that.}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
And
public RelayCommand<PersonForListDto> EditPersonRowCommand
{
get
{
return editPersonRowCommand ??
(editPersonRowCommand =
new RelayCommand<PersonForListDto>(param => this.EditPersonRow(param), this.editPersonRowCommandCanExecute));
}
}
With above, your VM would be cleaner and can easily be unit tested.
I need to open a new view (item details) on mouse double click in ListView in UWP using MVVM. In WPF I used a command with a parameter and EventTrigger but Microsoft does not recommended to use it in UWP:
Triggers, EventTrigger, Actions and BeginStoryboard are not commonly used. These API mainly exist for compatibility in XAML originally used for Microsoft Silverlight...For events in control templates, use visual states and VisualStateManager.
As I understood it is used when you need to change visual state of the control but I need to open a new view.
How can I use VisualStateManager for my purpose?
There is how my XAML looked in WPF:
<ListBox x:Name="PersonsListControl" Grid.RowSpan="3" Grid.Row="0" Grid.Column="2"
ItemsSource="{Binding Path=PersonsProvider}"
ItemsPanel="{StaticResource PersonsListPanelTemplate}"
ItemTemplate="{StaticResource PersonsListItemTemplate}"
SelectedItem="{Binding SelectedPerson}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction
Command="{Binding GetPersonDetailsCommand}"
CommandParameter="{Binding SelectedPerson}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
In UWP you can use {x:Bind ...} :
<ListBox ...
DoubleTapped="{x:Bind HandleDoubleTapped}" />
And in your ViewModel just create a method :
public void HandleDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
// your logic
}
References :
DoubleTapped
ListBox
EDIT:
#JörgenSigvardsson pointed out that x:Bind do not bind directly to the DataContext and you should create a proxy property/properties to access particular data from your page.
More on that can be read here
I need to modify a desktop application which uses WPF, MVVM and Behaviors for event handling. I've got a task to implement Drag&Drop for a button. If the user presses the button it will popup a file-save window but if the user clicks and drags it, it should display a file icon and the let the user drop it into an Explorer window to save it there.
I've already added namespaces:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:behaviors="clr-namespace:MyApplication.Desktop.Client.Behaviors"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:command="http://www.galasoft.ch/mvvmlight"
I've also added XAML code into the button:
<Button Grid.Column="2"
Command="{Binding SaveAttachmentCommand}"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Converter={StaticResource boolToVisibilityConverter}}"
Style="{StaticResource AttachmentSaveButtonStyle}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<command:EventToCommand Command="{Binding LeftMouseButtonDownCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<i:Interaction.Behaviors>
<behaviors:FrameworkElementDragBehavior>
</behaviors:FrameworkElementDragBehavior>
</i:Interaction.Behaviors>
</Button>
But I don't know how to tell the behavior class (FrameworkElementDragBehavior) which events to handle and how to handle them (what functions to call).
I've read some tutorials but I'm still confused.
I had to do Drag and drop with MVVM two months ago.
After some research, personnaly, the best way to achieve that is to work with the "GongSolutions DragDrop" library.
It's very simple and it's perfect for what you're looking for.
For example, in a treeview:
<TreeView ItemsSource="{Binding LstCat}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DragAdornerTemplate="{StaticResource DragAdorner}">
//Treeview Structure
</TreeView>
From there you can do Drag&Drop in the treeview. You can add a dragAdorner too (an image next to your pointer when you are dragging something)
In the viewModel you can tell the behaviour of the dragging or Dropping by implementing an interface which comes with the library. This way you can access to the data your dragging.
For example:
public void DragOver(IDropInfo dropInfo)
{
if (dropInfo.Data is Category && dropInfo.TargetItem is Rubrique)
{
return;
}
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
dropInfo.Effects = DragDropEffects.Move;
}
Here is the link of the library if you're interested:
https://github.com/punker76/gong-wpf-dragdrop
What I would like to figure out is two things, how to get a trigger occurring when a user control's visibility is changed and passing the value of visibility through as a parameter.
For whatever reason the trigger doesn't seem to be firing. I have only just added in the ControlVisible parameter to show what I would like to happen, when testing it was not there and just had a messagebox inside to catch when visibility changed, as in the commented out method.
I am using 4.0 with Visual Studio 2010
Main Window View which contains the user control
<Window x:Class="bt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:bt"
xmlns:ctrls="clr-namespace:bt.Controls"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
mc:Ignorable="d">
<Grid>
<ctrls:Login Visibility="{Binding DataContext.Vis,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},Converter={StaticResource BooleanToVisibilityConverter}}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="IsVisibleChanged">
<ei:CallMethodAction MethodName="VisibleTrigger" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ctrls:Login>
</Grid>
</Window>
UserControl View Model:
namespace bt.Controls
{
class LoginViewModel
{
public LoginViewModel()
{
}
public void VisibleTrigger(bool ControlVisible)
{
if (ControlVisible)
{
MessageBox.Show("Start timer");
}
else
{
MessageBox.Show("Stop timer");
}
}
//public void VisibleTrigger()
//{
// MessageBox.Show("Changed");
//}
}
}
First, we need to set TargetObject property to viewmodel/DataContext, because method to be invoked is available in the viewmodel :
......
<i:Interaction.Triggers>
<i:EventTrigger EventName="IsVisibleChanged">
<ei:CallMethodAction MethodName="VisibleTrigger" TargetObject="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
......
Second, EventTrigger doesn't seems to work specifically with IsVisibleChanged event. So code snippet above works for other event, but not IsVisibleChanged. We can find a workaround in the answer to this SO question, by using PropertyChangedTrigger to listen to Visibility property changed, instead of listening to IsVisibleChanged event :
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger Binding="{Binding Visibility, ElementName=MyControlName}">
<ei:CallMethodAction MethodName="VisibleTrigger" TargetObject="{Binding}"/>
</ei:PropertyChangedTrigger>
</i:Interaction.Triggers>
Third, CallMethodAction doesn't seems to provide a way to pass parameter to the method. To be able to invoke a method with parameter we better use InvokeCommandAction instead of CallMethodAction as suggested here and also suggested by #Rohit in your previous question.
How I can bind one of my buttons on control to X Button that closes the window ?
I just want to create cancel button that just closes the window.
I am using MVVM in my code.
If possible to do it only in xaml, I just dont have any special code with the button click.
You can just call the Close() method, which will close the window.
private void MyButton_Click(object s, RoutedEventArgs e)
{
Close();
}
If it's WPF (and provided I remember right) you can just use CallMethodAction from the parent as a behavior and utilize Close() method via just XAML. Something like;
Parent Window x:Name="window"
namespaces;
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
-
<Button Content="Cancel">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction
TargetObject="{Binding ElementName=window}"
MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Hope this helps.
MVVM solution without code-behind could also look like this:
View:
<Button Content="Cancel" Command="{Binding CloseWindowCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
ViewModel:
public ICommand CloseWindowCommand
{
get
{
return new RelayCommand<Window>(SystemCommands.CloseWindow);
}
}
But SystemCommands is from .net-4.5 so if you rock in some older version of .net you can also use following.
public ICommand CloseWindowCommand
{
get
{
return new RelayCommand<Window>((window) => window.Close());
}
}