Toggle ToolTip of a Button control using only XAML - silverlight

The title explains the end-goal.
Right now the problem is that I can't even change the ToolTip with one click to something else.
My XAML is:
<Button x:Name="btn" Height="24" Margin="107,59,109,0" VerticalAlignment="Top">
<ToolTipService.ToolTip>
<TextBlock>Hi</TextBlock>
</ToolTipService.ToolTip>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=btn}" PropertyName="ToolTipService.ToolTip" Value="Jeroen"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Image x:Name="icon" Height="16" Source="Images/FaceSad.png" Stretch="Fill" Width="16"/>
</Button>
I am doing this for a designer who works exclusively in XAML and I don't want to use C#. Is that possible? And does anyone know how to do this with XAML trigger and EventTriggers?

The ChangePropertyAction is a fairly simplistic object it literally looks for a public property called "ToolTipService.ToolTip". It does not parse this name to determine that its an Attached property.
Your code currently relies on the tool tip service creating a Tooltip control for you but if you create one yourself you can give it a name that can be referenced. You can then manipulate its Content property. Like this:-
<Button x:Name="btn" Height="24" Margin="107,59,109,0" VerticalAlignment="Top">
<ToolTipService.ToolTip>
<ToolTip x:Name="btnToolTip">
<TextBlock>Hi</TextBlock>
</ToolTip>
</ToolTipService.ToolTip>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=btnToolTip}" PropertyName="Content">
<ei:ChangePropertyAction.Value>
<TextBlock>Jeroen</TextBlock>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<Image x:Name="icon" Height="16" Source="Images/FaceSad.png" Stretch="Fill" Width="16"/>
</Button>

Just as note:
This didn't help me reach my goal as stated in the title and what I ended up doing was this:
Firstly, on the UserControl I added:
< ... ToolTipService.ToolTip="Happy" Tag="Happy|Sad" MouseLeftButtonUp="ToolTipSwitch" />
Then, I have this function in my code behind:
private void ToolTipSwitch(object sender, RoutedEventArgs e)
{
// ...
// Whatever is in the code for a MouseButtonUp
// ...
#region ToolTip switch code
UserControl tooltipParent = sender as UserControl;
Char[] pipe = {'|'};
String[] tooltips = tooltipParent.Tag.ToString().Split(pipe, StringSplitOptions.None);
if (ToolTipService.GetToolTip(tooltipParent).ToString() == tooltips[0])
ToolTipService.SetToolTip(tooltipParent, tooltips[1]);
else if (ToolTipService.GetToolTip(tooltipParent).ToString() == tooltips[1])
ToolTipService.SetToolTip(tooltipParent, tooltips[0]);
#endregion ToolTip switch code
}

Related

wpf xaml page navigation without code behind

I am designing a prototype and want to navigate between pages in a wpf application.
Currently I am using code behind by handling events f.e.
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Page2 page = new Page2();
this.NavigationService.Navigate(page);
}
This works, but it is unpleasant if you think of using MVVM later.
Is there a way to do this directly in xaml considering using a buttons click event?
Below code will show Page1 and Page2 in the Frame upon clicking the buttons.
It also shows Page2 on startup in the Frame.
Change namespace name in the Source of Frame below.
<Frame x:Name="frame" Content="Frame" Margin="80,80,144,122" Source="/WpfNavigation;component/Page2.xaml"/>
<Button x:Name="button" Content="Page1" HorizontalAlignment="Left" Margin="80,40,0,0" VerticalAlignment="Top" Width="75" Background="#FFF9F9F9">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Source" TargetObject="{Binding ElementName=frame}">
<ei:ChangePropertyAction.Value>
<System:Uri>Page1.xaml</System:Uri>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button x:Name="button1" Content="Page2" HorizontalAlignment="Left" Margin="192,40,0,0" VerticalAlignment="Top" Width="75">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=frame}" PropertyName="Source">
<ei:ChangePropertyAction.Value>
<System:Uri>Page2.xaml</System:Uri>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
To use Blend assemblies :
Add reference to System.Windows.Interactivity and Microsoft.Expression.Interactions and following namespaces :
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
If we want to navigate using Button present in the Page itself, then we can do that using Source property of NavigationService object of Page class.
Page1.xaml :
<Button Content="Go to page2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Source" TargetObject="{Binding NavigationService, RelativeSource={RelativeSource AncestorType={x:Type Page}, Mode=FindAncestor}}">
<ei:ChangePropertyAction.Value>
<System:Uri>Page2.xaml</System:Uri>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
If you are using a button you can use a command binding. Your view model will have a property ICommand that does your navigate for you
<Button Command="{Binding Navigate}"/>

Wpf, How to find Element name from one user control in another

<TextBox x:Name="DescriptionTextBox"
Text="{Binding SelectedEntity.Description,
ValidatesOnExceptions=True}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus" SourceName="DescriptionTextBox">
<ei:CallMethodAction MethodName="RaiseCanExecuteChanged"
TargetObject="{Binding Command, ElementName=SaveButton}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<userControls:SaveCloseUserControl />
Inside this user control
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button Width="50"
Command="{Binding SaveCommand}"
Content="Save" />
<Button Width="50"
Command="{Binding CloseCommand}"
Content="Close" />
</StackPanel>
The problem here is that this TargetObject="{Binding Command, ElementName=SaveButton}" does not find SaveButton because its inside of userControls:SaveCloseUserControl
I looked online and I found a solution (from 2009) that required code behind, is there not an easy way of doing this stuff today?
Regards
Edit
Based on #huzle answer I did this in the code behind of the user control
public object CreateButton { get { return CreateChild; } }
and in xaml i added a name to the save button.
so the my final version looks like this
<TextBox x:Name="DescriptionTextBox"
Text="{Binding SelectedEntity.Description,
ValidatesOnExceptions=True}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus" SourceName="DescriptionTextBox">
<ei:CallMethodAction MethodName="RaiseCanExecuteChanged"
TargetObject="{Binding CreateButton.Command,ElementName=MySaveCloseUserControl}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<userControls:SaveCloseUserControl x:Name="MySaveCloseUserControl"/>
Give your "SaveCloseUserControl" a public properties for the "SaveButton" named "InnerSaveButton" and bind with ElementName to your SaveCloseUserControl and use the Path for getting to the Command of your inner button.
<TextBox x:Name="DescriptionTextBox"
Text="{Binding SelectedEntity.Description,
ValidatesOnExceptions=True}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus" SourceName="DescriptionTextBox">
<ei:CallMethodAction MethodName="RaiseCanExecuteChanged"
TargetObject="{Binding InnerSaveButton.Command, ElementName=MySaveCloseUserControl}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<userControls:SaveCloseUserControl X:Name="MySaveCloseUserControl"/>

Why is the click event of a button inside a DataGrid is not fired?

I am using MVVM pattern and MVVM Light to convert an event into a command, in my XAML I have this code:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="+" Padding="0,0,0,0" Height="Auto">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding MyCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
In my view model I have this code:
private RelayCommand<SelectionChangedEventArgs> _myCommand;
public RelayCommand<SelectionChangedEventArgs> MyCommandCommand
{
get { return _myCommand ?? (_myCommand = new RelayCommand<SelectionChangedEventArgs>(myCommandCommand)); }
}
private void myCommand(SelectionChangedEventArgs e)
{
// code
}
But the click event is not fired. However, I am using this way for every button in my application and when the button is into a user control or window, the event is fired.
Thanks.
Change your binding, the DataContext that it's trying to look for is the DataContext for the Template which might be different depending on your structure.
Change it to this
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.MyCommand}" /
Besides the above answer, I found that it was also necessary to specify ClickMode="Press" in Xaml.
<Button Content="" Focusable="True" FontFamily="Segoe UI Symbol" FontSize="16" Background="{StaticResource HeroLightGray}" Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center"
ClickMode="Press"
Command="{Binding DataContext.CopyMetadataSourceAsync, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" CommandParameter="{Binding .}" />
I do not recall having to do this in the past, but after spending hours troubleshooting, converting RelayCommand to IAsyncCommand, etc. this is the only thing that worked. In fact, I couldn't even get a regular code-behind "Click event" method to fire unless I included that ClickMode="Press"!

Pivot control MVVM-Light-EventToCommand SelectedIndex sends previous index

I get the index of the Pivot item I'm leaving, not the Pivot Item that I am going to.
I have searched for some time now and can think of some solutions like using an event in the view and then sending a message using MVVM-Light to the ViewModel. But I'd rather not do that, I'd rather stick to this current implementation, slightly different of course.
Any help is appreciated
My xaml:
<controls:Pivot x:Name="ContentPivot" Margin="12,0,12,0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command ="{Binding SelectSlideCommand}"
CommandParameter="{Binding SelectedIndex, ElementName=ContentPivot}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<controls:PivotItem x:Name="PivotItem0" Header="0">
<Image Source="{Binding ViewingSlides[0].Path}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</controls:PivotItem>
<controls:PivotItem x:Name="PivotItem1" Header="1">
<Image Source="{Binding ViewingSlides[1].Path}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</controls:PivotItem>
<controls:PivotItem x:Name="PivotItem2" Header="2">
<Image Source="{Binding ViewingSlides[2].Path}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</controls:PivotItem>
</controls:Pivot>
and c#:
public RelayCommand<int> SelectSlideCommand;
SelectSlideCommand = new RelayCommand<int>((pivotItem) => SelectSlideAction(pivotItem));
void SelectSlideAction(int parameter)
{
currentIndex = parameter;
UpdateViewingSlides();
Debug.WriteLine("SelectSlideAction: " + parameter);
}
Do you have `SelectedItem Property in your Control... you can hook it up in Your ViewModel to a property in a TwoWay binding Mode to always get the updated Value (Then you will not need this command).... Also yoou can try
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command ="{Binding SelectSlideCommand}"
CommandParameter="{Binding SelectedItem, ElementName=ContentPivot}" />
</i:EventTrigger>
</i:Interaction.Triggers>

Silverlight: Can I put interaction triggers into resources for reuse?

I have a bunch of controls using identical interactions triggers across multiple user controls and viewmodels. Is it possible to place these triggers somehow into a resource dictionary for reuse? Here's an example of what a control might look like.
<TextBox x:Name="FirstName" Grid.Row="1" Grid.Column="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cal:ActionMessage MethodName="KeyPressed" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBox x:Name="Initial" Grid.Row="1" Grid.Column="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cal:ActionMessage MethodName="KeyPressed" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBox x:Name="LastName" Grid.Row="1" Grid.Column="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cal:ActionMessage MethodName="KeyPressed" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
The cal: namespace is from the Caliburn.Micro MVVM framework and probably isn't relevant to this question.
Its not possible to re-use a single instance of Interaction.Triggers in a resource because it becomes attached a control. That attachment becomes part of its state hence a single instance can't be shared by multiple controls.
You would need to include the Interaction.Triggers in a template so that multiple instances are created. I guess something like the following might work, (warning air code).
<UserControl.Resources>
<DataTemplate x:key="MyTextBox">
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cal:ActionMessage MethodName="KeyPressed" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</DataTemplate>
</UserControl.Resources>
...
<ContentPresenter x:Name="FirstName" Grid.Row="1" Grid.Column="1" ContentTemplate="{StaticResource MyTextBox}" />
<ContentPresenter x:Name="Initial" Grid.Row="1" Grid.Column="1" ContentTemplate="{StaticResource MyTextBox}" />
<ContentPresenter x:Name="LastName" Grid.Row="1" Grid.Column="1" ContentTemplate="{StaticResource MyTextBox}" />
It my opinion that this sort of stuff isn't worth it. The Interaction Triggers stuff is really aimed at empowering the designer rather than the developer. A designer isn't that worried that there is some repeatition in the "code".
i have something useful in case you don't have solve completely your issue about reusing Interaction Triggers...
<TextBox x:Name="FirstName" KeyDown="_KeyDown">
...
private void TextBlock_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
Here goes something you wanna reuse for all your TextBox controls..
}
Maybe the better option is to use a design pattern like MVVM and EventToCommand Interactions by GalaSoft

Resources