I've got this goto functionality in my DataGrid. This functionality I would like to keep out of my ViewModel and out of code-behind, so the following attachment could be perfect, however...
The user enters a line(item) number then when the user clicks the GotoButton it brings the item into view.
<Grid>
<TextBox x:Name="GotoTextbox" Text="{Binding GotoLineNumber, UpdateSourceTrigger=PropertyChanged}" />
<Button Name="GotoButton" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<helpers:TargetedTriggerActionGotoButton TargetObject="{Binding ElementName=GenericDataGrid}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
Here is the TargetedTriggerAction class.
public class TargetedTriggerActionGotoButton : TargetedTriggerAction<DataGrid>
{
protected override void Invoke(object parameter)
{
this.Target.SelectedGridItem = GotoLineNumber - 1;
this.Target.SelectedGridIndex = GotoLineNumber.GetValueOrDefault() - 1;
}
}
I would like to somehow pass the text in from GotoTextbox, is there some binding I can do?. How could I achieve this?
As we spoke in comments
to allow parameters to be passed in, one should implement additional property in your TargetedTriggerAction
public class TargetedTriggerActionGotoButton : TargetedTriggerAction<DataGrid>
{
protected override void Invoke()
{
this.Target.SelectedGridItem = GotoLineNumber - 1;
this.Target.SelectedGridIndex = GotoLineNumber.GetValueOrDefault() - 1;
}
//property used as parameter
public object Parameter {get;set;}
}
and then in your xaml
<Grid>
<TextBox x:Name="GotoTextbox" Text="{Binding GotoLineNumber, UpdateSourceTrigger=PropertyChanged}" />
<Button Name="GotoButton" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<helpers:TargetedTriggerActionGotoButton TargetObject="{Binding ElementName=GenericDataGrid}" Parameter="{Binding ElementName="/*desiredName*/",Path="/*neededValue*/"}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Related
How do i trigger the SourceUpdate of my wpf listview?
This is my xaml
<ListView DataContext="{StaticResource vmInstance}" SelectedItem="{Binding selectedItem}" BorderBrush="Beige" BorderThickness="1" ItemsSource="{Binding lstExternal}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged" >
<cmd:EventToCommand Command="{Binding Path=ItemChanged}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="SourceUpdated">
<cmd:EventToCommand Command="{Binding Path=SourceUpdated}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.View >
</ListView.View>
</ListView>
ViewModel:
public static RelayCommand SourceUpdated { get; set; }
//on initialize
SourceUpdated = new RelayCommand(SourceUpdateEvent);
public void SourceUpdateEvent()
{
Console.WriteLine("Updated");
}
Why does SourceUpdateEvent doesn't run?
Am I using the wrong event?
Thank you
want to execute a method when the itemsource of the listview is changed or updated
Then bind the ItemsSource property of the ListView to a source property of your view model
<ListView ItemsSource="{Binding YourCollection}" ...>
...and invoke your command in the setter of this source property:
private ObservableCollection<YourType> _sourceCollection;
public ObservableCollection<YourType> SourceCollection
{
get { return _sourceCollection; }
set
{
_sourceCollection = value;
RaisePropertyChanged();
SourceUpdated.Execute(null);
}
}
This is the (only) correct way to solve this using the MVVM pattern.
I am trying to bind a click on button event in WPF to a command defined in a View Model, here is how I am doing that for now :
In the xaml code :
<Grid>
<Button Content="Module A" Background="Green" FontWeight="Bold">
<i:Interaction.Triggers>
<i:EventTrigger EventName="click">
<i:InvokeCommandAction Command="{Binding ChargeModuleDCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
and in the ViewModel class :
class ModuleAViewModel
{
public DelegateCommand<object> ChargeModuleDCommand { get; set; }
public ModuleAViewModel()
{
ChargeModuleDCommand = new DelegateCommand<object>(LaunchDModule);
}
private void LaunchDModule(object parm)
{
Console.WriteLine("I am in the function");
}
}
but it does not work. I've tried to do it as specified in this question : How to trigger ViewModel command for a specific button events
but it does not work either.
Is there any way that I can make it work ?
<Button
Command="{Binding ChargeModuleDCommand}"
Content="Module A"
Background="Green"
FontWeight="Bold"
/>
If ModuleAViewModel is the Button's DataContext, that should work.
I am having an scenario where I show a window to the user and ask them to choose anything by left click on it. See attached pix
So in this Window I have corresponding WindowViewModel following a Prism 6.1.0 framework. I want to bind this click event to the Grid instead of Binding with the each TextBlock each. Is it possible?
if yes, I tried this. In the grid Control my code is this.
<Grid x:Name="Locals1">
<Grid.InputBindings>
<MouseBinding MouseAction="LeftClick"
Gesture="LeftClick" Command="{Binding MouseCommand,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
CommandParameter="{Binding ElementName=Locals1,
Path=SelectedItem, Mode=TwoWay}"
/>
</Grid.InputBindings>
<TextBlock Focusable="True" text="textblock1" />
<TextBlock Focusable="True" text="textblock2" />
<TextBlock Focusable="True" text="textblock3" />
<TextBlock Focusable="True" text="textblock4" />
</Grid>
And in the WindowViewModel I have a code like this.
public WindowViewModel(IEventAggregator eventAggregator)
{
MouseCommand = new DelegateCommand<string>(OnConnection);
}
private void OnConnection(string obj)
{
...
}
But I don't get the TextBlock.Text value in that OnConnection method. Is it really so tough? What I know about WPF and MVVM that we can handle the child click event in the parent control itself. This will reduce duplicate codes.
I know I am doing something definitely wrong. But I don't know what exactly. How can I pass this value from WindowViewModel to the MainWindowViewModel?
I can achieve the same functionality using a binding each in all the textblocks but that will not serve the purpose of Prism. basically all the text Block click events functionality is same only the value of the textblock will be different.
thanks
Honestly, i don't like my answer, but:
You use Grid, not DataGrid and similar, so what is SelectedItem in you context?!
I cant invent how to use pretty binding in this case, so i changed command
public DelegateCommand<Object> MouseCommand { get; set; }
public WindowViewModel(IEventAggregator eventAggregator)
{
MouseCommand = new DelegateCommand<object>(OnConnection);
}
and
private void OnConnection(object obj)
{
var text = GetTextFromClickOnGrid(obj);
}
private string GetTextFromClickOnGrid(object obj)
{
var grid = obj as Grid;
if (grid != null)
{
var mousePos = Mouse.GetPosition(grid);
var itemUnderMouse = VisualTreeHelper.HitTest(grid, mousePos);
var textBlock = itemUnderMouse.VisualHit as TextBlock;
if (textBlock != null)
{
return textBlock.Text;
}
}
var textBlockUnderMouse = Mouse.DirectlyOver as TextBlock;
if (textBlockUnderMouse != null)
{
return textBlockUnderMouse.Text;
}
return string.Empty;
}
and xaml
<Grid Grid.Row="1" x:Name="Locals1">
<Grid.InputBindings>
<MouseBinding MouseAction="LeftClick"
Gesture="LeftClick"
Command="{Binding MouseCommand,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
CommandParameter="{Binding ElementName=Locals1}"
/>
</Grid.InputBindings>
<TextBlock Focusable="True" Text="textblock1" Height="30" Width="100" Margin="115,45,302,84" />
<TextBlock Focusable="True" Text="textblock2" Height="30" Width="100" Margin="115,10,302,119"/>
<TextBlock Focusable="True" Text="textblock3" Height="30" Width="100" Margin="10,45,407,84"/>
<TextBlock Focusable="True" Text="textblock4" Height="30" Width="100" Margin="10,10,407,119"/>
</Grid>
i think you looking this:
<i:Interaction.Triggers>
<i:EventTrigger EventName="LeftClick">
<command:EventToCommand
Command="{Binding Main.MouseCommand ,
Mode=OneWay,
Source={StaticResource Locator}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
binding what you want. Any event to almost any command.
ViewModelLocator:
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
}
I have a Textbox and for that textbox I have attached a keydown event. Everything is working fine but I just noticed that when i'm pressing the 'Backspace' and 'Delete' Key, the binding command is not being called.
My View xaml file :-
<TextBox x:Name="textBox" Width="500" Text="{Binding TextBoxText, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cmd:EventToCommand Command="{BindingPath=TextBoxKeyDownEvent}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
My ViewModel cs file :-
//TextBox Key Down Event Handler
private DelegateCommand _textBoxKeyDownEvent;
public ICommand TextBoxKeyDownEvent
{
get
{
if (_textBoxKeyDownEvent == null)
{
_textBoxKeyDownEvent = new DelegateCommand(TextBoxKeyDownEventHandler);
}
return _textBoxKeyDownEvent;
}
set { }
}
Can somebody give me some suggestion
EDIT:
You have to use PreviewKeyDown the it works. KeyDown is not fired on Space and Delete. If you ignore MVVM and put the handler of KeyDown in codebehind it will also fail.
How about binding the Text-Property to a string in you viewmodel?
I build a fast, simple example of my idea.
Result
Text from the TextBox on the left side is simply populated to the Textblock on the right side.
View
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding TextBoxValue, UpdateSourceTrigger=PropertyChanged}" Width="250"/>
<StackPanel Orientation="Horizontal">
<TextBlock>"</TextBlock>
<TextBlock Text="{Binding TextBoxValue, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock>"</TextBlock>
</StackPanel>
</StackPanel>
</Window>
ViewModel
public class MainWindowViewModel : INotifyPropertyChanged
{
private string textBoxValue;
public string TextBoxValue
{
get { return textBoxValue; }
set
{
textBoxValue = value;
OnTextBoxValueChanged();
RaisePropertyChanged();
}
}
void OnTextBoxValueChanged()
{
// you logic here, if needed.
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
you most use PreviewKeyDown event.
Like this:
<EventSetter Event="PreviewKeyDown" Handler="TextBox_PreviewKeyDown"/>
Edit: You are correct - the default behavior is not executed. You should use ec8ors solution, which is much better anyway:
<TextBox x:Name="textBox" Width="500" Text="{Binding TextBoxText, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewKeyDown">
<i:InvokeCommandAction Command="{Binding TextBoxKeyDownEvent, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Original:
You can use InputBindings to call your command when "special" keys have been pressed:
<TextBox x:Name="textBox" Width="500" Text="{Binding TextBoxText, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cmd:EventToCommand Command="{BindingPath=TextBoxKeyDownEvent}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox.InputBindings>
<KeyBinding Command="{Binding TextBoxKeyDownEvent}" Key="Delete" />
<KeyBinding Command="{Binding TextBoxKeyDownEvent}" Key="Back" />
</TextBox.InputBindings>
</TextBox>
In this listbox i display contact names.
<ListBox x:Name="Items" Margin="36,38,78,131">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="lol" Text="{Binding Path=ContactName}" Style="{StaticResource PhoneTextSmallStyle}"
Width="Auto" TextAlignment="Center" FontWeight="Bold" Foreground="White" VerticalAlignment="Bottom" TextWrapping="Wrap"/>
<Button x:Name="ShowName">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="delete" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I get contacts from local DB
public List<FBContacts> listContactDatas { get; set; }
Items = new BindableCollection<FBContacts>();= new BindableCollection<FBContacts>();
public void GetContacts()
{
using(MyDataContext mydb = new MyDataContext(DBConnectionstring))
{
var items = from ContactsList Name in mydb._contacts select Name;
foreach (var toDoItem in items)
{
Items.Add(new FBContacts()
{
ContactName = toDoItem.Name
});
}
}
}
user can delete any contact if he press button.
public void delete()
{
Items.RemoveAt(/* index*/);
}
so how i can get index of choosen contact?
It is easier if you pass the clicked FBContacts to delete method :
<Button x:Name="ShowName">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="delete">
<cal:Parameter Value="{Binding}" />
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Then you can remove by FBContacts object instead of index :
public void delete(FBContacts item)
{
Items.Remove(item);
}
Bind the currently selected item's index to a separate property:
<ListBox x:Name="Items" SelectedIndex="{Binding SelectedListIndex}" Margin="36,38,78,131">
Of course, SelectedListIndex must be defined as property of type int that fires PropertyChanged in the Viewmodel.
Then, you can easily access the selected item's index everywhere within the Viewmodel:
public void delete()
{
Items.RemoveAt(SelectedListIndex);
}