TemplateBinding as InvokeCommandAction CommandParameter not working? - wpf

I'm trying to write a reusable template for a control I want to use several times in my window. In it I have some commands with CommandParameter. The CommandParameter is an object I want to pass via the "Tag" property of this template.
For the "MenuItem" CommandParameter and the StackPanel "DataContext" this is working just fine. But for the "MouseDown" event (or "PreviewMouseDown", I tried both) via InvokeCommandAction I'm only getting a null as parameter. It is firing the command and calls the right method, but the parameter is always null.
Here my Xaml (shortend):
<Window.Resources>
<ControlTemplate x:Key="FavControl" TargetType="ContentControl">
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu IsEnabled="{Binding FavContextMenuEnabled}">
<MenuItem Header="Bearbeiten"
Command="{Binding FavContextMenuEditCmd}"
CommandParameter="{TemplateBinding Tag}" />
<MenuItem Header="Löschen"
Command="{Binding FavContextMenuDeleteCmd}"
CommandParameter="{TemplateBinding Tag}" />
<Separator IsEnabled="{Binding UserisDev}" />
<MenuItem Header="Entwicklung"
Command="{Binding FavContextMenuStartDevCmd}"
CommandParameter="{TemplateBinding Tag}"
IsEnabled="{Binding UserisDev}" />
<MenuItem Header="Starte Objekt"
Command="{Binding FavContextMenuRunObjectCmd}"
CommandParameter="{TemplateBinding Tag}"
IsEnabled="{Binding UserisDev}" />
</ContextMenu>
</StackPanel.ContextMenu>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<i:InvokeCommandAction Command="{Binding FavoriteClickCmd}" CommandParameter="{TemplateBinding Tag}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<StackPanel Orientation="Vertical" DataContext="{TemplateBinding Tag}">
<Image Source="{Binding FavImage, UpdateSourceTrigger=PropertyChanged,Mode=OneWay}"
HorizontalAlignment="Center" VerticalAlignment="Center" Height="70" Width="70" />
<TextBlock Text="{Binding Description}" HorizontalAlignment="Center" />
</StackPanel>
</StackPanel>
</ControlTemplate>
</Window.Resources>
<!-- ... -->
<ContentControl Grid.Row="0" Grid.Column="0" Template="{StaticResource FavControl}" Tag="{Binding FavoriteArray[0]}" />
<ContentControl Grid.Row="0" Grid.Column="1" Template="{StaticResource FavControl}" Tag="{Binding FavoriteArray[1]}" />
<ContentControl Grid.Row="0" Grid.Column="2" Template="{StaticResource FavControl}" Tag="{Binding FavoriteArray[2]}" />
<!-- ... -->
I need this parameter in the code behind method to know which object to work with. So, how can I pass this object? Why is the TemplateBinding not working? Can anybody tell me, what I'm doing wrong?
P.S.: Sorry for errors, english is not my native language :)

Instead of TemplateBinding, use a RelativeSource binding to TemplatedParent.
<b:InvokeCommandAction Command="{Binding FavoriteClickCmd}"
CommandParameter="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}" />

Related

How to bind to property of sibling control?

In a listbox of items, one item can be designated as the primary item. In the template, I have a button bound to a parameterized command (the specific item in the collection is the parameter passed to the command in the datacontext) that is visible only if the item is not currently the primary item. If the item IS the primary item, I want to display a static image. Since I can't bind the image to the command to witch I am binding the button, I figured I could bind the Visibility property of the image to the "inverse" of the Visibility property of the button. (i.e. when the button is visible, the image is hidden and vice versa.) But I can't figure out how to do this. The button is a sibling of the image within a grid within the template. Here's my template...
<DataTemplate>
<StackPanel Orientation="Vertical">
<Grid>
<Grid.ColumnDefinitions>
...
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=FormattedNumber}" Style="{StaticResource FieldDataTextBlock}" FontWeight="Bold" />
<!-- How can I make this image aware of the following button's state? -->
<Image Grid.Column="2" Source="/Resources/Star.Pressed.png" Visibility="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path={}}" Width="20" Height="20" />
<Button Grid.Column="2" x:Name="btnMakePrimary" Style="{StaticResource StarButton}" Command="{Binding ElementName=lstPhoneNumbers, Path=DataContext.MakePrimaryPhoneNumberCommand}" CommandParameter="{Binding}" ToolTip="Set as display number." Visibility="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}, Converter={StaticResource BoolVisibility}}" />
<Button Grid.Column="4" Style="{StaticResource DetailsButton}" Command="{Binding ElementName=lstPhoneNumbers, Path=DataContext.ViewPhoneNumberCommand}" CommandParameter="{Binding}" />
<Button Grid.Column="6" Style="{StaticResource DeleteButton}" Command="{Binding ElementName=lstPhoneNumbers, Path=DataContext.DeletePhoneNumberCommand}" CommandParameter="{Binding}" />
</Grid>
<Grid >
<Grid.ColumnDefinitions>
...
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=PhoneTypeString}" Style="{StaticResource FieldDataTextBlock}" />
<TextBlock Grid.Column="2" Text="(notes)" Foreground="Blue" ToolTip="{Binding Path=PhoneNumberNote}" Visibility="{Binding Path=HasNote, Converter={StaticResource BoolVisibility}}" />
</Grid>
</StackPanel>
</DataTemplate>
Either that, or is there a way to bind the image to a method in the parent datacontext that takes a parameter?
Thanks.
J
Never mind... I was able to make it happen via the ElementName attribute of the binding and fixing an issue with the included image resource which was messing with my visuals:
<Button Grid.Column="2" Name="btnMakePrimary" Style="{StaticResource StarButton}" Command="{Binding ElementName=lstPhoneNumbers, Path=DataContext.MakePrimaryPhoneNumberCommand}" CommandParameter="{Binding}" ToolTip="Set as display number." Visibility="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}, Converter={StaticResource BoolVisibility}}" />
<Image Grid.Column="2" Source="/Resources/Star.Pressed.png" Visibility="{Binding ElementName=btnMakePrimary, Path=IsEnabled, Converter={StaticResource BoolVisibilityReverse}}" Width="20" Height="20" />

Binding an image in a datatemplate

I'm trying to add a menu to my application, I'd like that my menu header is the currently connected user's picture.
My issue is that every binding works except the one for the image, from what I've understood on the net the DataTemplate I use to replace the menu header by a picture doesn't have access to it's parent Datacontext. I tried to use some stuff like relative source and other tricks but with no luck (certainely related to the fact that I'm new in mvvm and I obviously still have a lot to learn :) )
Here's the code I'm using :
<Grid DataContext="{Binding User}">
<DockPanel>
<Label Content="{Binding Strings.Hello, Source={StaticResource StringLocalizer} }" VerticalAlignment="Center" Padding="0,0,5,0" FontFamily="{DynamicResource Font_Normal}" FontSize="15" Foreground="White"/>
<TextBlock Text="{Binding DisplayName, TargetNullValue='Guest', FallbackValue='Guest'}" VerticalAlignment="Center" HorizontalAlignment="Left" FontFamily="{DynamicResource Font_Normal}" FontSize="15" Foreground="White"/>
<Menu Name="LogonMenu" Margin="10,0,0,0" DataContext="{Binding User}">
<MenuItem >
<MenuItem.HeaderTemplate>
<DataTemplate>
<!--<Image Source="{Binding Datacontext.Image,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Application}}, Converter={StaticResource ImageConverter}, FallbackValue={StaticResource DefaultUserIcon}}"/>-->
<Image DataContext="{Binding User}" Source="{Binding Path=Image, Converter={StaticResource ImageConverter}, FallbackValue={StaticResource DefaultUserIcon},Mode=TwoWay}"/>
</DataTemplate>
</MenuItem.HeaderTemplate>
<MenuItem Header="{Binding Strings.SignIn, Source={StaticResource StringLocalizer}}" Width="150" Margin="10,0,0,0" IsEnabled="{Binding Connected, Converter={StaticResource BoolInverterConverter}}" Visibility="{Binding RelativeSource={RelativeSource Mode=Self},Path=IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}" Command="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.SignInCommand}"/>
<MenuItem Header="{Binding Strings.SignOut, Source={StaticResource StringLocalizer}}" Margin="10,0,0,0" IsEnabled="{Binding Connected}" Visibility="{Binding RelativeSource={RelativeSource Mode=Self},Path=IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}" Command="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.SignOutCommand}"/>
<MenuItem Header="{Binding Strings.UserInformation, Source={StaticResource StringLocalizer}}" Margin="10,0,0,0" IsEnabled="{Binding Connected}" Visibility="{Binding RelativeSource={RelativeSource Mode=Self},Path=IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}"/>
</MenuItem>
</Menu>
</DockPanel>
</Grid>
In my case the image always shows the fallback picture, if someone could help me finding a way to have my user's picture it would be great.

Modifying TabControl's header

So I'm adding my views directly to the TabControl's Items collection at runtime (instead of creating TabItems around them and addings those TabItems to TabControl). The views expose a property (wrapper around a ViewModel property of the same name) named HasChanges that I want to bind to TabItem's Header to show a Asterisk (*) sign to identify tabs with unsaved changes, just like VS does. I have already tried using DataTemplates but am having trouble accessing the view object in the DataTemplate. What's the correct way of doing this? Here's one of my several attempts:
<TabControl.ItemTemplate>
<DataTemplate DataType="UserControl">
<StackPanel Orientation="Horizontal" Margin="0" Height="22">
<TextBlock VerticalAlignment="Center" Text="{Binding HeaderText, RelativeSource={RelativeSource AncestorType=UserControl}}" />
<TextBlock Text="*" Visibility="{Binding HasChanges, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource B2VConverter}}" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
Note that I'm trying two different binding methods for the two TextBlocks, none of which is working. My views inherit from UserControl and expose properties HasChanges and HeaderText.
OK. I solved it myself. For anyone else trying to implement a VS-like Close button and unsaved changes asterisk, here's the template:
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0" Height="22">
<TextBlock VerticalAlignment="Center" Text="{Binding RelativeSource={RelativeSource AncestorType=TabItem}, Path=Content.HeaderText}" />
<TextBlock Text=" *" ToolTip="Has unsaved changes" Visibility="{Binding Content.DataContext.HasChanges, RelativeSource={RelativeSource AncestorType=TabItem}, Converter={StaticResource B2VConverter}}" />
<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Width="18" Height="18"
Margin="6,0,0,0" Padding="0" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center"
Command="{Binding DataContext.TabClosingCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"
VerticalAlignment="Center" Focusable="False">
<Grid Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Line StrokeThickness="3" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="Gray" X1="1" Y1="1" X2="9" Y2="9" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Line StrokeThickness="3" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="Gray" X1="1" Y1="9" X2="9" Y2="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Button>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
Results in an elegant drawing-based button with a flat-look. Your View must implement Boolean HasChanges and HeaderText properties, plus you need to define a BooleanToVisibilityConverter in your resources section, named B2VConverter.

Silverlight: Adding ChangePropertyAction to DataTemplate

I was playing around, experimenting and googling this for the last hour and cannot figure out the problem. Basically, I have a listbox, each item has textblock and checkbox. I need to change text of textblock to green next to the checked checkbox. The code below is the best that I could come up with, but it doesn't work. Any help is appreciated.
<DataTemplate x:Key="ListBoxTemplate">
<Grid
Width="80">
<TextBlock
x:Name="textBlock"
HorizontalAlignment="Left"
Text="{Binding Data, Converter={StaticResource DataConverter}}"
Foreground="#FF859FAF"
FontSize="12"
TextWrapping="Wrap"
Width="50"
Margin="0"
Grid.Row="10" />
<CheckBox
x:Name="Btn6"
IsChecked="{Binding IsSelected, Mode=TwoWay}"
Cursor="Hand"
Padding="0"
HorizontalAlignment="Right"
Height="32"
VerticalAlignment="Bottom"
RenderTransformOrigin="0.5,0.5"
Width="26"
d:LayoutOverrides="GridBox">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="textBlock">
<ei:ChangePropertyAction
TargetName="textBlock"
TargetObject="{Binding ElementName=textBlock}"
PropertyName="Width"
Value="100">
</ei:ChangePropertyAction>
<i:InvokeCommandAction
Command="{Binding SelectDataCommand, Source={StaticResource ControlViewModel}}"
CommandName="SelectDateRangeCommand"
CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
(sorry for my bad english)
This do not exactly answer your question but an easy alternative would be to bind IsSelected with the TextBox.Foreground and create a converter that return a SolidColorBrush green when the value is true

WPF : InputBindings on a StackPanel

I want to put a command on a ListBoxItem. The ListBoxItem use a DataTemplate composed of a StackPanel (containing an Image and a TextBlock, both using Binding). I want that the doubleclick on that ListBoxItem fire the command.
I have tried this :
<DataTemplate>
<StackPanel>
<StackPanel.Resources>
<CommonUI:CommandReference x:Key="DoubleClickCommand" Command="{Binding Path=DefaultCommand}" />
</StackPanel.Resources>
<StackPanel.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{StaticResource DoubleClickCommand}" />
</StackPanel.InputBindings>
<Image Source="{Binding Path=Thumbnail, IsAsync=True}" IsHitTestVisible="False"/>
<TextBlock Text="{Binding Path=Name}" IsHitTestVisible="False">
</StackPanel>
</DataTemplate>
I have also tried to put the Command Resources on a StackPanel containing this StackPanel, without any change.
I am certain of my binding because when I put the InputBindings part on the TextBlock, it works.
Thanks
Try handling the event in the ListBox instead of the StackPanel:
<ListBox>
<ListBox.Resources>
<CommonUI:CommandReference x:Key="DoubleClickCommand" Command="{Binding Path=DefaultCommand}" />
</ListBox.Resources>
<ListBox.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{StaticResource DoubleClickCommand}" />
</ListBox.InputBindings>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Path=Thumbnail, IsAsync=True}" />
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox>
My code finally looks like this :
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<CommonUI:CommandReference x:Key="DoubleClickCommand" Command="{Binding Path=DefaultCommand}" />
</StackPanel.Resources>
<StackPanel.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{StaticResource DoubleClickCommand}" />
</StackPanel.InputBindings>
<Image Source="{Binding Path=Thumbnail, IsAsync=True}" />
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
Thanks anyway, Mr Poulin.

Resources