How to set MouseBinding in ItemsControl - wpf

I want to call click command on TextBlock, and i do that like this
<TextBlock Text="New Project">
<TextBlock.InputBindings>
<MouseBinding Command="{Binding Path=NewProject}" MouseAction="LeftClick"/>
</TextBlock.InputBindings>
</TextBlock>
And that is work, but i want to do the same in ItemsControl, so
<ItemsControl Margin="8" ItemsSource="{Binding Path=Projects}" Grid.Row="1" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Name}" Style="{StaticResource ClickableTextBlock}" >
<TextBlock.InputBindings>
<MouseBinding Command="{Binding Path=OpenTasks}" MouseAction="LeftClick"/>
</TextBlock.InputBindings>
</TextBlock>
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And that doesn't work, command is not called. How to do that?

The implicit DataContext inside ItemTemplate is the current data item. But the OpenTasks is not part of each data item. As you said in your comment, it's part of your viewmodel. So in this case you have to set some explicit source with RelativeSource for the Binding. It helps walk up the visual tree and target the source you want. In this case you need the DataContext of the ItemsControl. The code should be like this:
<MouseBinding Command="{Binding DataContext.OpenTasks,
RelativeSource={RelativeSource AncestorType=ItemsControl}}"
MouseAction="LeftClick"/>

Related

pass contextmenu parent as CommandParameter

i have a HierarchicalDataTemplate for a TreeViewItem, in the template i have a contextmenu and i want to pass as CommandParameter the ContextMenu parent - i.e the TreeViewItem owner that was right clicked at the moment, is there a way to do that?
here is my template:
<HierarchicalDataTemplate
x:Key="ServerTemplate"
DataType="{x:Type models:Server}"
ItemsSource="{Binding Channels}"
ItemTemplate="{StaticResource ChannelTemplate}">
<StackPanel
Tag="{Binding DataContext,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}}}"
Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu
FontSize="14"
FontFamily="Arial">
<MenuItem
Header="{x:Static p:Resources.ServerOperations_CommunicationSettings}"
Command="{Binding PlacementTarget.Tag.ServerCommunicationSettingCommand, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ContextMenu}}}"
CommandParameter="{Binding Path=Parent, RelativeSource={RelativeSource Mode=Self}}">
</MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
<Image
Source="{Binding ImageURL, Converter={StaticResource StringToImageConverter}}"
Margin="0,0,2,0"
Height="25"
Width="25"/>
<TextBlock
Text="{Binding ServerName}"
Foreground="White"/>
</StackPanel>
</HierarchicalDataTemplate>
thanks for the help
You can get the TreeViewItem by getting PlacementTarget of ContextMenu which will be StackPanel and its TemplatedParent will be ContentPresenter and its TemplatedParent will be TreeViewItem. So this will work:
CommandParameter="{Binding Path=PlacementTarget.TemplatedParent.TemplatedParent,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ContextMenu}}}"
PlacementTarget (StackPanel) --> TemplatedParent (ContentPresenter) --> TemplatedParent (TreeViewItem)
Ideally it's not a good idea to pass UI components to ViewModel. You should pass data i.e. DataContext of TreeViewItem as you can always play with that.
In case you want to pass Server instance i.e. DataContext of TreeviewItem, you can simply do "{Binding}" since MenuItem will inherit it from StackPanel.
CommandParameter="{Binding}"

How to edit an item in the treeview in wpf

My TreeView ItemTemplate looks like below.
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Connections}">
<WrapPanel >
<CheckBox VerticalAlignment="Center" Command="{Binding UpdateConnections}" CommandParameter="{Binding}" IsChecked="{Binding Status, Mode=TwoWay}" Focusable="False" Style="{StaticResource ResourceKey=TreeView_CheckBox_Style}"></CheckBox>
<TextBlock Text="{Binding Name}" Style="{StaticResource ResourceKey=treeTextBoxStyle}" />
</WrapPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
I am using MVVM. I would like to edit the TextBox if my tree item starts with "s".
I basically double click my TreeViewItem and I should be able to type in it.
How do i do it ?

EventToCommand in ItemControl in Windows Phone 8

I cannot understand why I cant call eventToCommand in my datatemplate inside ItemControl. According to this post I should implement it in the dataTemplate, but the command is never invoked. This is the eventToCommand im trying to insert in my code.
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<cmd:EventToCommand Command="{Binding ItemSelectedCommand, Mode=OneWay}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
And this is the code im trying to instert it to. However, as the comments say, it is never invoked when put in the dataTemplete. The problem is not the viewModel, the command works in the panelTemplate.
<ItemsControl ItemsSource="{Binding GroupRow1}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<!-- COMMAND WORKS HERE, but cannot locate which item has been pressed -->
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="#FF1756C3" Height="173" Width="173" Margin="12,0,0,0" >
<!-- COMMAND DOES NOT WORK HERE?!?! -->
<StackPanel>
<TextBlock Text="{Binding LineOne}" /> <!-- These bindings work -->
<TextBlock Text="{Binding LineTwo}" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
How do I find out which Item has been pressed?
Many answers, but none of them worked for me. I redesigned my solution and got rid of the eventtocommand which wasent working. Instead I made buttons with custom content to look the same as my border.
Simpler code and a better solution.
<ItemsControl ItemsSource="{Binding GroupRow1}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button CommandParameter="{Binding LineOne}" Height="195" Width="195" Margin="0,0,-10,0" Click="Button_Click_2" Background="#FF1756C3">
<StackPanel>
<TextBlock Text="{Binding LineOne}" />
<TextBlock Text="{Binding LineTwo}" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I had a similar problem before, what solved it for me was referencing the VM in the binding. Try to set PassEventArgsToCommand to false if you want to receive the item instead of the EventArgs
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<cmd:EventToCommand PassEventArgsToCommand="False"
CommandParameter="{Binding}"
Command="{Binding Path=VM_Name_Here.Command_Name_Here, Source={StaticResource Locator}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Edit- If you are using MVVM Light, in the app.xml you should have something like:
xmlns:vm="clr-namespace:PhoneApplication.ViewModel (namespace where your ViewModelLocator is under)
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="true" />
You are telling the binding to look in the ViewModelLocator for a particular VM.
Hope it helps,
Regards.
I think it has to do with your binding in the data template. How is the view created? I think the command is a property of the viewModel, not GroupRow1 which is the collection for your items control. You need to bind to the command in your ViewModel. SO if your view is a usercontrol, the following should work. (if its of another type then change the ancestortype)
<cmd:EventToCommand Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.ItemSelectedCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBlock}},Path=Name}"/>
The command parameter adds the name of the textblock for example, you could change that to any property of the textblock.
I would personally have SelectedItem property in my viewmodel that can be accesses as an object from the ItemSelectCommand
Hope this helps.
The answer is pretty obvious. In ItemsPanelTemplate your binding source is still the ViewModel and your command stays at your ViewModel. But in DataTemplate you are iterating over GroupRow1 items and your binding source is individual item. If you want to use your command there, you have to bind from the relative source in example:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<cmd:EventToCommand Command="{Binding ViewModel.ItemSelectedCommand, RelativeSource={RelativeSource AncestorType=UserControl}, Mode=OneWay}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>

WPF MVVM customizing ListView cell to insert hyperlink and change text color

I have a GridView cell in a ListView that is defined as a TextBlock and bound to a string on my ViewModel. I want to be able to change parts of the text into hyperlinks and part into different colors programatically.
Here is the XAML for the GridView cell:
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="#dfdfdf" HorizontalAlignment="Stretch">
<TextBlock Text="{Binding Data}" Margin="3"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
And here's what it looks like at the moment:
An example of the text that is bound would be:
<color:#ff0000>Test item</color>
Test item 2
<link:http://www.google.com>Test hyperlink</link>
I have no problem with the regex to parse the bound text and pull out the required information but how would I go about changing the TextBlock into different colors and add a hyperlink?
Thanks in advance
You can put multiple Run elements inside a TextBlock and style then however you like.
Here is an example with a working hyperlink which even supports MVVM :)
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="#dfdfdf" HorizontalAlignment="Stretch">
<TextBlock Margin="3">
<Run Text="{Binding Data}" />
<Run Text="Some more data" Background="Red" />
<Run Text="Click Me" Foreground="Blue" TextDecorations="Underline" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<local:EventToCommand Command="{Binding LinkClickCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Run>
</TextBlock>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
Note:
The Interaction.Triggers are from System.Windows.Interactivity and the EventToCommand from MVVMLight.
Similar to Blachshma's answer, but using a hyperlink right in there with the Run objects:
<TextBlock>
<Run Text="{Binding Data}" />
<Run Text="Some more data" Background="Red" />
<Hyperlink Command="{Binding Path=Command}">
<TextBlock Text="{Binding Path=Text}"/>
</Hyperlink>
</TextBlock>
Clearly I have a Command and Text properties to bind to for the hyperlink. Will look exactly the same, but will also respond to the command's CanExecute predicate (hyperlinks go grey).

binding a command inside a listbox item to a property on the viewmodel parent

I've been working on this for about an hour and looked at all related SO questions.
My problem is very simple:
I have HomePageVieModel:
HomePageVieModel
+IList<NewsItem> AllNewsItems
+ICommand OpenNews
My markup:
<Window DataContext="{Binding HomePageViewModel../>
<ListBox ItemsSource="{Binding Path=AllNewsItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>
<Hyperlink Command="{Binding Path=OpenNews}">
<TextBlock Text="{Binding Path=NewsContent}" />
</Hyperlink>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
The list shows fine with all the items, but for the life of me whatever I try for the Command won't work:
<Hyperlink Command="{Binding Path=OpenNewsItem, RelativeSource={RelativeSource AncestorType=vm:HomePageViewModel, AncestorLevel=1}}">
<Hyperlink Command="{Binding Path=OpenNewsItem, RelativeSource={RelativeSource AncestorType=vm:HomePageViewModel,**Mode=FindAncestor}**}">
<Hyperlink Command="{Binding Path=OpenNewsItem, RelativeSource={RelativeSource AncestorType=vm:HomePageViewModel,**Mode=TemplatedParent}**}">
I just always get :
System.Windows.Data Error: 4 : Cannot find source for binding with reference .....
Update
I am setting my ViewModel like this? Didn't think this would matter:
<Window.DataContext>
<Binding Path="HomePage" Source="{StaticResource Locator}"/>
</Window.DataContext>
I use the ViewModelLocator class from the MVVMLight toolkit which does the magic.
Slightly different example but,
I found that by referencing the parent container (using ElementName) in the binding you can get to it's DataContext and its subsequent properties using the Path syntax. As shown below:
<ItemsControl x:Name="lstDevices" ItemsSource="{Binding DeviceMappings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<ComboBox Text="{Binding Device}" ItemsSource="{Binding ElementName=lstDevices, Path=DataContext.AvailableDevices}" />
...
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
There's two issue working against you here.
The DataContext for the DataTemplate is set to the item the template is displaying. This means you can't just use binding without setting a source.
The other issue is that the template means the item is not technically part of the logical tree, so you can't search for ancestors beyond the DataTemplate node.
To solve this you need to have the binding reach outside the logical tree. You can use a DataContextSpy defined here.
<ListBox ItemsSource="{Binding Path=AllNewsItems}">
<ListBox.Resources>
<l:DataContextSpy x:Key="dataContextSpy" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>
<Hyperlink Command="{Binding Source={StaticResource dataContextSpy}, Path=DataContext.OpenNews}" CommandParameter="{Binding}">
<TextBlock Text="{Binding Path=NewsContent}" />
</Hyperlink>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
try something like this
<Button Command="{Binding DataContext.YourCommand,RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
he can't find your command binding inside the listbox because you set a diffrent datacontext than the viewmodel for that listbox
So looks like you are trying to give the proper DataContext to the HyperLink so as to trigger ICommand.
I think a simple element name binding can solve this.
<Window x:Name="window" DataContext="{Binding HomePageViewModel../>
<ListBox ItemsSource="{Binding Path=AllNewsItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>
<Hyperlink DataContext="{Binding DataContext ,ElementName=window}" Command="{Binding Path=OpenNews}">
<TextBlock Text="{Binding Path=NewsContent}" />
</Hyperlink>
</TextBlock>
</StackPanel>
</DataTemplate>
The AncestorType checks only for Visual-Types not for ViewModel types.
Well, it's a little bit late, I know. But I have only recently faced the same problem. Due to architectural reasons I decided to use a static viewmodel locator instead of the dataContextSpy.
<UserControl x:Class="MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:locator="clr-namespace: MyNamespace"
DataContext="{Binding Source={x:Static locator:ViewModelLocator.MyViewModel}}" >
<ListBox ItemsSource="{Binding Path=AllNewsItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>
<Hyperlink Command="{Binding Source={x:Static locator:ViewModelLocator.MyViewModel},
Path=OpenNews}"
CommandParameter="{Binding}">
<TextBlock Text="{Binding Path=NewsContent}" />
</Hyperlink>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
The static viewmodel locator instantiates the view model:
namespace MyNamespace
{
public static class ViewModelLocator
{
private static MyViewModelType myViewModel = new MyViewModelType();
public static MyViewModelType MyViewModel
{
get
{
return myViewModel ;
}
}
}
}
Using this workaround is another way to bind from a data template to a command that is in the viewmodel.
The answer from #Darren works well in most cases, and should be the preferred method if possible. However, it is not a working solution where the following (niche) conditions all occur:
DataGrid with DataGridTemplateColumn
.NET 4
Windows XP
...and possibly in other circumstances too. In theory it should fail on all versions of Windows; but in my experience the ElementName approach works in a DataGrid on Windows 7 upwards but not XP.
In the following fictional example, we are trying to bind to an ICommand called ShowThingCommand on the UserControl.DataContext (which is the ViewModel):
<UserControl x:Name="ThisUserControl" DataContext="whatever...">
<DataGrid ItemsSource="{Binding Path=ListOfThings}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Thing">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Command="{Binding ElementName=ThisUserControl, Path=ShowThingCommand}"
CommandParameter="{Binding Path=ThingId}"
Content="{Binding Path=ThingId}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</UserControl>
Due to the DataTemplate not being in the same VisualTree as the main control, it's not possible to reference back up to the control by ElementName.
To solve this, the little known .NET 4 and above {x:Reference} can be used. Modifying the above example:
<UserControl x:Name="ThisUserControl" DataContext="whatever...">
<DataGrid ItemsSource="{Binding Path=ListOfThings}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Thing">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Command="{Binding Source={x:Reference ThisUserControl}, Path=ShowThingCommand}"
CommandParameter="{Binding Path=ThingId}"
Content="{Binding Path=ThingId}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</UserControl>
For reference, see the following stackoverflow posts:
Question 19244111
Question 5834336
...and for an explanation of why ElementName doesn't work in this circumstance, see this blog post which contains a pre-.NET 4 workaround.
<ListBox xmlns:model="clr-namespace:My.Space;assembly=My.Assembly"
ItemsSource="{Binding Path=AllNewsItems, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>
<Hyperlink Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.(model:MyNewsModel.OpenNews), Mode=OneWay}"
CommandParameter="{Binding Path=., Mode=OneWay}">
<TextBlock Text="{Binding Path=NewsContent, Mode=OneWay}" />
</Hyperlink>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Resources