i want to bind an event using MetroEventToCommand library.
http://metroeventtocommand.codeplex.com/
It calls the method however it doesn't give the control as parameter. It's says it's null.
this is what i have.
<ScrollViewer x:Name="test">
<ListView/>
<metroEventToCommand:EventToCommandManager.Collection>
<metroEventToCommand:EventToCommand Command="{Binding RefreshCommand}" Event="ViewChanging" CommandParameter="{Binding ElementName=test, Mode=TwoWay}"/>
</metroEventToCommand:EventToCommandManager.Collection>
</ScrollViewer>
public RelayCommand<ScrollViewer> RefreshCommand { get; set; }
private void init()
{
RefreshCommand = new RelayCommand<ScrollViewer>(Refresh);
}
private void Refresh(ScrollViewer o)
{
if (o != null)
{
}
}
thanks in advance.
OK... not quite an answer, but I can't put code in a comment. I put together a quick app using MVVM Light and using the Interactivity library instead of metroToEventCommand.
I can pass it from hooking up an event handler (of course)
private void onViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
vm.RefreshCommand.Execute(this.test);
}
I can also get it to fire the command WITH the parameter using the Loaded event or PointerEntered event
<ScrollViewer x:Name="test" >
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="PointerEntered">
<Core:InvokeCommandAction Command="{Binding RefreshCommand, Mode=OneWay}" CommandParameter="{Binding ElementName=test}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<ListView x:Name="myList"/>
</ScrollViewer>
However, the Interactivity dll throws an exception (Cannot add instance of type 'Microsoft.Xaml.Interactions.Core.EventTriggerBehavior' to a collection of type 'Microsoft.Xaml.Interactivity.BehaviorCollection'.) when trying to use the ViewChanging, ViewChanged, DirectManipulationCompleted, etc. events - whether a parameter is used or not. ManipulationCompleted does not fire the command (I've seen hints that commands cannot be bound to routed events).
So, it would seem that the concept works - but only for certain event types. I'm sorry that isn't a solution to your challenge, but hopefully it is some additional information to help your attempts.
Related
I have the following xaml view:
<UserControl x:Class="MyViews.PersonView"
xmlns:views="clr-namespace:MyViews"
[...]
>
[...]
<dxb:BarManager x:Name="MainBarManager">
<dxb:BarManager.Items>
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
Command="{Binding PrintPersonsCommand}"
CommandParameter="{Binding PersonsCardView, ElementName=CardUserControl}"
/>
</dxb:BarManager.Items>
<Grid>
<Grid.RowDefinitions>
[...]
</Grid.RowDefinitions>
<views:CardView x:Name="CardUserControl" Grid.Row="2"/>
</Grid>
[...]
</UserControl>
The CardView is defined as follows:
<UserControl x:Class="MyViews.CardView"
[...]>
[...]
<dxg:GridControl ItemsSource="{Binding Persons}" SelectedItems="{Binding SelectedPersons}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionMode="MultipleRow">
[...]
<dxg:GridControl.View>
<dxg:CardView x:Name="PersonsCardView"
[...]
CardTemplate="{StaticResource DisplayCardTemplate}"
PrintCardViewItemTemplate="{StaticResource PrintCardTemplate}"/>
</dxg:GridControl.View>
[...]
</dxg:GridControl>
</UserControl>
The PrintPersonsCommand is defined as follows in my ViewModel:
public class PersonViewModel
{
public PersonViewModel(...)
{
[...]
PrintPersonsCommand = new Prism.Commands.DelegateCommand<DataViewBase>(PrintPersons, CanPrintPersons);
}
public Prism.Commands.DelegateCommand<DataViewBase> PrintPersonsCommand { get; private set; }
private void PrintPersons(DataViewBase view)
{
_printService.ShowGridViewPrintPreview(view);
}
private bool CanPrintPersons(DataViewBase view)
{
return true;
}
}
Now, when I click the Print button, the above PrintPersons method is always fed with null. How do I pass CardUserControl.PersonsCardView in my MyViews.PersonView xaml above, how do I pass that PersonCardView to my command? In other words, how do I fix
CommandParameter="{Binding PersonsCardView, ElementName=CardUserControl}"
to make it work?
Currently, the only solution I've found to this problem is to replace the Command and CommandParameter with
ItemClick="OnPrintBtnClick"
and then in the PersonView's code-behind file to do:
private void OnPrintBtnClick(object sender, ItemClickEventArgs e)
{
var ctxt = DataContext as PersonViewModel;
ctxt.PrintPersonsCommand.Execute(CardUserControl.PersonsCardView);
}
That works but I can't believe there is no other way. I'm not happy with that solution because I don't have the benefits of using the Command any more, like e.g. the automatic evaluation of the Command's CanExecute method. I could also put the CardView's xaml code in the PersonView.xaml but I like my controls to be in separate files because I have the feeling it's more structured and each user control has its own responsibilities which can nicely be split into separate files. Also, that solution binds my view to my view model too tightly.
Can someone help me out please?
Without changing your existing view and viewmodel hierarchy, I was able to pass the GridControl.View to the PersonViewModel using the Tag property
You can assign the CardView to the Tag property at the bottom of your CardView UserControl, and then access this Tag as CommandParameter.
CardView UserControl
<UserControl x:Class="MyViews.CardView"
[...]>
[...]
<dxg:GridControl ItemsSource="{Binding Persons}" SelectedItems="{Binding SelectedPersons}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionMode="MultipleRow">
[...]
<dxg:GridControl.View>
<dxg:CardView x:Name="PersonsCardView"
[...]
CardTemplate="{StaticResource DisplayCardTemplate}"
PrintCardViewItemTemplate="{StaticResource PrintCardTemplate}"/>
</dxg:GridControl.View>
[...]
</dxg:GridControl>
<UserControl.Tag>
<Binding ElementName="PersonsCardView"/>
</UserControl.Tag>
</UserControl>
Print Button Xaml:
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
Command="{Binding PrintPersonsCommand}"
CommandParameter="{Binding ElementName=CardUserControl, Path=Tag}"
/>
Based on the valuable input of Insane, I came up with the following two cleaner fixes:
Code-behind solution
In the PersonView, use the ItemClick event handler on the Print button:
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
ItemClick="OnPrintBtnClick"/>
Adapt the corresponding code-behind file like this:
public partial class PersonView : UserControl
{
readonly IPrintService _printService;
public PersonView(IPrintService printService)
{
_printService = printService;
InitializeComponent();
}
private void OnPrintBtnClick(object sender, ItemClickEventArgs e)
{
_printService.ShowGridViewPrintPreview(CardUserControl.PersonsCardView);
}
}
Because I want to gray-out the Print button when there is no selection, I still need to add some code to make that happen. I can get it by
1. updating the button code to
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
ItemClick="OnPrintBtnClick" IsEnabled="{Binding CanPrintPersons}"/>
refreshing the CanPrintPersons property in the PersonViewModel upon Persons selection change
That's it.
CardViewModel solution
In that solution, we have a PersonView with its underlying PersonViewModel and a CardView with its underlying CardViewModel. I will not describe that solution with all the details as it is overkill in my situation but for the sake of completeness, I'll give the main points. Upon clicking the Print button on the PersonView, the PersonViewModel's PrintCommand is called. That command emits a Print event to the CardViewModel which in turn calls its own PrintCommand. That latter command calls
_printService.ShowGridViewPrintPreview(View);
where the View is a CardViewModel's property that is set upon CardView loading with e.g.
<dxmvvm:Interaction.Behaviors>
<dxmvvm:EventToCommand EventName="Loaded" Command="{Binding ViewLoadedCommand}" CommandParameter="{Binding ElementName=PersonsCardView}" />
</dxmvvm:Interaction.Behaviors>
Because I have two child views I want to print, I'd need to add a view model for each one of those. In addition, those two view models plus the PersonViewModel need access to the list of Persons to be printed. In particular, they need a shared access to the same data, so that they are synchronized. A simple way to do that is explained here and is totally doable. But I think it is not worth the trouble for the simple use case I have as it adds more complexity than necessary.
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.
I have encountered a rather strange problem using the GalaSoft - MVVM Light Toolkit in a Windows Phone 8 project.
Suddenly (after merging some stuff) all my EventToCommand calls are not working anymore. They worked before. I already tried to remove the MvvmLight toolkit and reference it again with Nuget, but the result stays the same.
One example:
MainMenuPage.xaml
<phone:PhoneApplicationPage
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:commands="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP8"
....>
<phone:PhoneApplicationPage.DataContext >
<Binding Source="{StaticResource MainMenuViewModel}"/>
</phone:PhoneApplicationPage.DataContext>
<!-- catch back key press -->
<i:Interaction.Triggers>
<i:EventTrigger EventName="BackKeyPress">
<commands:EventToCommand
Command="{Binding BackKeyPressCommand}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
MainMenuViewModel.cs
// back key press command
public RelayCommand<object> BackKeyPressCommand { get; set; }
public MainMenuViewModel()
{
BackKeyPressCommand = new RelayCommand<object>(
BackKeyPress,
(o) => true
);
...
}
private void BackKeyPress(Object o)
{
// handle back key press
}
This worked perfectly before, but now the BackKeyPress(Object o) Method never gets called anymore.
This happens for all EventToComamnd calls.
If I remove the xmlns tags, Resharper suggests to add this:
xmlns:command="http://www.galasoft.ch/mvvmlight"
Result of that:
The name "EventToCommand" does not exist in the namespace "http://www.galasoft.ch/mvvmlight"
Anyone encountered a similar problem or has an idea what could have caused this?
These namespaces are correct.
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:commands="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP8"
But your PhoneApplicationPage has wrong DataContext.
DataContext="{Binding Path=MainMenuViewModel, Source={StaticResource Locator}}"
MainMenuViewModel is property in the ViewModelLocator:
public MainMenuViewModel MainMenuViewModel
{
get
{
return ServiceLocator.Current.GetInstance<MainMenuViewModel>();
}
}
Btw the argument of BackKeyPress is CancelEventArgs. You should use:
public RelayCommand<CancelEventArgs> BackKeyPressCommand { get; private set; }
this.BackKeyPressCommand = new RelayCommand<CancelEventArgs>(this.BackKeyPress)
private void BackKeyPress(CancelEventArgs e)
{
}
Windows Phone 8.1
Windows 8.1 Behavior SDK: How to use InvokeAction with InputConverter to pass arguments to a Command
Microsoft has developed it's own EventToCommand functionality. It's located in Behaviors SDK. Someone on stackoverflow told to get this SDK via Nuget. If you can't find the package in NuGet - get it in Add reference dialog.
(My add reference dialog may differs from original because of Productivity Power Tools extension)
Here is example of simple usage:
<ListBox ItemsSource="{Binding Persons, Mode=OneWay}"
SelectedItem="{Binding SelectedPerson, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{Binding DisplayPersonCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListBox>
Say I have a WPF TextBox:
<TextBox Text="{Binding Foo}" />
Is there a way to execute some ICommand after the property Foo is updated by the TextBox (i.e. is updated specifically by the TextBox rather than some other control or code)?
I don't want to use the SourceUpdated event of the binding because I want to avoid "code-behind".
You do realise the MVVM police won't come and get you if do end up having some code behind? Using event to command etc is essentially just coding in XAML instead of in C# anyway. Either way you need to hook an event up to a command because the control doesn't expose a command for what you are after.
I think this is what you after, let me know if I'm on the wrong track:
You want to know when the update to Foo comes from the textbox and the textbox only. i.e. if the update to Foo comes from some code you don't want the command to run, correct?
If so:
<TextBox Name="Fred" Text="{Binding Foo, NotifyOnSourceUpdated=True}" SourceUpdated="Fred_SourceUpdated"/>
Then in the "evil" code behind have:
private void Fred_SourceUpdated(object sender, DataTransferEventArgs e)
{
}
In that method you can cast the datacontext of the view into the viewmodel and call whatever command you want. The source updated event will not get called if something else updates Foo.
If you really want to not have code behind as you mention in your OP then you could do an attached behaviour but that is massive overkill for a "nonsense" requirement. The fact that you logic depends on the whether or not the change comes from the textbox means that your view is more than a view already. This approach still allows you to have your command code fully testable in you VM.
2nd edit
You could also look at using the source updated event in XAML via the event to command rather than text changed
You can try making a method that does whatever you want to be done and call that method after the raisepropertychanged() is called. For example
public void MyMethod()
{
//Do whatever;
}
And then in your property getter setter:
public string MyText
{
get { return _MyText; }
set
{
_MyText = value;
RaisePropertyChanged("MyText")
// THen call that method
MyMehtod();
}
}
Syntax may be off, I'm use to doing vb lately. Hope this helps, there are other ways though if you need other options.
Edit 2:
<Textbox Text="{Binding Foo}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding TextChangedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Where TextChangedCommand is in your viewmodel
Not sure if TextChanged is an EventName, I dont memorize all the available event types
Assuming you have implemented INotifyPropertyChanged, you just need to handle the PropertyChanged event. You execute the command in your event handler. This event handler goes in your ViewModel (not your code behind).
Edit
Here is an example of how the EventToCommand behavior in MVVM Light works. You can use this allow any event to be handled with a command.
<Rectangle Fill="White"
Stroke="Black"
Width="200"
Height="100">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<cmd:EventToCommand Command="{Binding TestCommand,
Mode=OneWay}"
CommandParameter="{Binding Text,
ElementName=MyTextBox,
Mode=OneWay}"
MustToggleIsEnabledValue="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>
Edit 2
Another idea would be to always use a method when changing Foo via code. This way, you know that any change to Foo that doesn't use the method must have been changed by the user.
private bool _isFooUpdating;
private void SetFoo(string value)
{
_isFooUpdating = true;
Foo = value;
_isFooUpdating = false;
}
public string Foo
{
get { return _foo; }
set
{
if (_foo = value) return;
_foo = value;
OnFooChanged();
OnPropertyChanged("Foo");
}
}
private void OnFooChanged()
{
if (_isFooUpdating) return;
FooChangedCommand.Execute();
}
In my WP7 application I have ListBox control that binds with List<ItemTemplate> collection. On each ListBoxItem I have Click event which navigates to DisplayItem.xaml. Each ItemTemplate object has Id property which has to be passed to DispalyItem.xaml. I know that I can pass this value from Click EventHandler to DisplayItem.xaml using QueryString but how do I pass it from ListBox item to EventHandler ?
<ListBox x:Name="listBoxItems">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="red" Width="30" Height="30"></Ellipse>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Status}" FontSize="35" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<HyperlinkButton Content="{Binding ItemContent}" Name="itemButton" Click="itemButton_Click"/>
</StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="edit"/>
<toolkit:MenuItem Header="delete"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Id property is not mentioned in the above code because I just simply didnt know where to place it. Generally I want to know how can I get Id property back to the click EventHandler ? I'm sorry if this question is basic for you but I'm new to that and i wasn't sure how to google that.
If you're really new to Windows Phone 7, you may want to stop using the "Click" event and instead use the ListBox.SelectionChanged event. If you are bound to List<MyObject>, you could do the following:
In your XAML:
<ListBox SelectionChanged="NavigateToMyDetail" ... >
Then in the code behind, you would have something like this:
private void NavigateToMyDetail(object sender, SelectionChangedEventArgs e)
{
// Make sure that the ListBox change wasn't due to a deselection
if(e.AddedItems != null && e.AddedItems.Count == 1)
{
MyObject selectedItem = (MyObject)e.AddedItems[0];
// Now you have access to all your MyObject properties
// and you can pass that to your new page as a parameter
NavigationService.Navigate(new Uri("DisplayItem.xaml?ItemID=" + selectedItem.id.ToString(), UriKind.Relative));
}
}
And you can get that ID with the following code (probably in your "OnNavigatedTo" method).
string myItemID = null;
if(this.NavigationContext.QueryString.ContainsKey("ItemID"))
myItemID = NavigationContext.QueryString["ItemID"];
Hope that helps. The other way to try to get it is to give your ListBox a x:Name and then references it in your Click handler like:
private void MyClickHandler(object sender, System.Windows.RoutedEventArgs e)
{
MyObject selectedObject = (MyObject)MyListBoxName.SelectedItem;
}
There is a much simpler solution if you use data binding with an MVVM viewmodel behind it.
Simply bind you view to a property in the view model for the listbox "Source" and then also do the same for the ListBox "SelectedItem" or "SelectedIndex" properties, then you will have all you need accessible where ever you needed.
Only think to be aware of (as I'm uncertain if it ever got fixed) is to fixed the selected index property when an item has been selected, if you do not reset it to -1 then if the user returns to the list they cannot select the same item. (do this in the codebehind for the click event)
Also if you use MVVM and databinding you can enact an action from the change of the Selected item rather than using Code behind to drive the direction, always an option to keep things simple (but not mandatory)
I have also came to my own solution. I'm not sure If its correct bit its certainly solving my problem for now.
I found this CommandParameter property of object HyperlinkButton. I bound my MyObject.Id property value to it.
<HyperlinkButton Content="{Binding ItemContent}" Click="itemButton_Click" CommandParameter="{Binding Id}" />
Then in my EventHandler i said:
private void itemButton_Click(object sender, RoutedEventArgs e)
{
HyperlinkButton butt = sender as HyperlinkButton;
NavigationService.Navigate(new Uri("/ViewItem.xaml?itemId=" + butt.CommandParameter.ToString(), UriKind.Relative));
}
It works as I need it to work but I'm not sure If i should use it in my applications in the future.