I use MVVM Light in my WPF-application.
I have some usercontrol. It passes element by elementname as commandparameter to command.
<UserControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding LoadCommand}" CommandParameter="{Binding ElementName=Image}"></cmd:EventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>
<image Name="Image" />
</UserControl>
In my MainView I use this usercontrols:
<Window>
<l:mycontrol />
<l:mycontrol />
</Window>
ViewModel
class MainViewModel:ViewModelBase
{
public MainViewModel()
{
LoadCommand = new RelayCommand<Image>(OnLoad);
}
private void OnLoad(Image image)
{
Draw(image,bitmap);
}
}
"Draw" is my function for writing on Image with some usercontrol's parameters.
Cause Images have the same x:name, my application draws only on the last image.
How else I can pass my element to command?
Related
I spend a lot of time to understand why it is not working, it just MUST work but it doesn't...
Let's imagine I have ItemsControl binded to ObservableCollection. Inside ItemsControl.ItemTemplate there are StackPanel with buttons, textblocks etc.
I want to bind ToolTip Opened event attached to StackPanel to my Command, and I need instance of model also.
<StackPanel.ToolTip>
<ToolTip>
<TextBlock Text="Example text"></TextBlock>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Opened">
<i:InvokeCommandAction Command="{Binding Path=DataContext.TooltipOpenCommand, ElementName=MapsControl}" CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ToolTip>
</StackPanel.ToolTip>
My C# code is:
public ICommand TooltipOpenCommand => new RelayCommand(ToolTipOpen);
public void ToolTipOpen(object obj)
{
MessageBox.Show("Test message");
}
(RelayCommand is just default Command example from internet with execute and CanExecute)
So, when I hover mouse on StackPanel, tooltip with "Example text" is appears but command is not executed, I can't ever understand why, what am I doing wrong?
By the way, to prove that it MUST work, check this code(it attached just to the same StackPanel in example above, but not to tooltip). AND THIS CODE IS WORKING!!! But the code above isn't...
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding Path=DataContext.TooltipOpenCommand, ElementName=MapsControl}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
I am a new in C# and MVVM approach, I created such TextBox
<TextBox x:Name="Tb_fps"
Grid.Column="0"
Text="{Binding FPS, UpdateSourceTrigger=PropertyChanged}"
x:FieldModifier="private"
Margin="4,4,0,4"
HorizontalAlignment="Stretch"/>
and as you can see I am using UpdateSourceTrigger=PropertyChanged, but additionally I would like to use LostFocus and GotFocus events...
But I can't with this implementation of DataBinding...
Question is - how to handle all this events on one TextBox view?
EDIT
Associated with #Joe H answer
You need to use an EventTrigger from the
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
namespace.
This allows the conversion of an event into a ICommand pattern. Also you don't need the EventSource as that is the default for triggering a binding. This would be the Xaml and the ViewModel code follows
<Grid>
<StackPanel>
<TextBox Text="{Binding Text}" Height="40" Width="80">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding LostFocusCommand}" />
</i:EventTrigger>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding GotFocusCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBox Text="Need something to change focus to" Height="40" Width="80" />
</StackPanel>
</Grid>
ViewModel:
public class MyWindowViewModel : INotifyPropertyChanged
{
private string _text;
public string Text
{
get => _text;
set
{
_text = value;
OnPropertyChanged();
}
}
public ICommand LostFocusCommand => new ActionCommand(ExecuteLostFocus);
private void ExecuteLostFocus()
{
Console.WriteLine("LostFocus");
}
public ICommand GotFocusCommand => new ActionCommand(ExecuteGotFocus);
private void ExecuteGotFocus()
{
Console.WriteLine("GotFocus");
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Also, you should be aware that the Text property in the ViewModel does not update until the TextBox loses focus. If you want to capture text as it changes a TextChanged handler is needed
You can bind to any event from XAML using the Microsoft.Xaml.Behaviors.Wpf package.
After installing the package, you must include the behaviors namespace (xmlns:i="http://schemas.microsoft.com/xaml/behaviors) to your XAML file. Then, you can use the <i:InteractionTrigger> markup in your control (which is a XAML behaviour).
This markup allows you to declare an EventTrigger, which works a bit like style triggers, if your familiar with this. You can specify the control event you want to hook up to, and then declare a TriggerAction markup to react to the event.
You will most likely want to use a TriggerAction of type InvokeCommandAction, that will bind to a command in your viewmodel.
Your code would look like this:
<TextBox
x:Name="Tb_fps"
Grid.Column="0"
Margin="4,4,0,4"
HorizontalAlignment="Stretch"
x:FieldModifier="private"
Text="{Binding FPS, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding LostFocusCommand}" />
</i:EventTrigger>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding GotFocusCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Edit: I changed System.Windows.Interactivity package to the Microsoft.Xaml.Behaviors.Wpf, which is a more recent and maintained package.
I've got the application with ListView which I populate with images names, weights, etc. and inside one of the cells should be 2 buttons or labels to user interaction. I've searched and tried what I find on the web, but nothing helps.
<StackPanel Orientation="Horizontal">
<Grid>
<Button
Grid.Row="0"
Command="{Binding ShowImageCommand}"
Content="Show">
</Button>
</Grid>
<Grid>
<Button Content="Annotate"
CommandParameter="{Binding}"
Command="{Binding DataContext.ShowImageCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:MainWindow}}}" />
</Grid>
Neither option for Annotate button or Show works. I've tried to do it like a label, but also no success.
<Label Content="Show" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown" >
<i:InvokeCommandAction Command="{Binding ShowImageCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Label>
Here is how it looks like in class:
public MainViewModel()
{
ShowImageCommand = new RelayCommand(ShowImageMethod);
}
public ICommand ShowImageCommand { get; set; }
public void ShowImageMethod()
{
RaisePropertyChanged("ShowImageCommand");
Messenger.Default.Send<NotificationMessage>(new NotificationMessage("It's fine"));
}
I'm new to C# and MVVM pattern, so mostly I just copy-paste code from Stack and trying to make it work. Other buttons on the grid, for example, to add images to ListView are implemented in the same way, but everything works for those. Please give me some hints.
Is your viewmodel bind to your view?
DataContext = new MainViewModel();
or
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
Like Isaudon said I haven't had
DataContext = new MainViewModel();
After I added this to MainWindow.xaml.cs is implementation works out:
<Button Content="Annotate"
CommandParameter="{Binding}"
Command="{Binding DataContext.ShowImageCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:MainWindow}}}" />
I am working on wizard kind of application, where I am maintaining the wizards in
a list named List<ViewmodelsForWizard>.
I have used same UserControl UCView to render two different pages by simply tweaking the ViewModel values stored in List<ViewmodelsForWizard>.
The problem is that SelectionChangedCommand for previous page gets fired on load of the next page. (Both pages uses same UserControl UCView and ViewModel)
MainWindow
<Grid DataContext="{Binding currentWizard}">
<Grid.Resources>
<DataTemplate DataType="{x:Type viewmodel}">
<local:UCView DataContext="{Binding}"/>
</DataTemplate>
</Grid.Resources>
<ContentControl Content="{Binding}" />
</Grid>
I have following ViewModel:
//all other properties and commands
private ICommand selectionChangedCommand;
public ICommand SelectionChangedCommand
{
get
{
if (selectionChangedCommand == null)
selectionChangedCommand = new DelegateCommand(OnSelectionChanged());
return selectionChangedCommand;
}
set
{
selectionChangedCommand = value;
}
}
//all other properties and commands
Selectionchanged in UCView
<ComboBox ItemsSource="{Binding items}"
DisplayMemberPath="data"
SelectedValue="{Binding selected}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Any help would be great. Thanks.
Is it possible to use MVVMLight Toolkits EventToCommand approach for handling Datagrid's AutoGeneratingColumn event? I tried the following approach, but it doesn't seem to work:
<i:Interaction.Triggers>
<i:EventTrigger EventName="AutoGeneratingColumn">
<mvvm:EventToCommand Command="{Binding TestCommand}"> </mvvm:EventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>
Yes it is.
I'd recommend setting your Xaml and Command Signature as I've outlined below.
XAML
<i:Interaction.Triggers>
<i:EventTrigger EventName="AutoGeneratingColumn">
<mvvm:EventToCommand Command="{Binding TestCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
C# - In your DataContext (ViewModel)
public void Test(AutoGeneratingColumnEventArgs e)
{
// handle however you need to here
}