wpf xaml page navigation without code behind - wpf

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}"/>

Related

How to change data context of Interaction Trigger

I have a text box A with an interaction trigger. The Data Context of the text box is a property in the view model. However, the ClearCommand in defined in the view Model. How can I change the Data Context of the Interaction Triggers or its Command to the view model it self.
Thank you
<TextBox Name="TextBoxA"Text="{Binding myObject.TextPrp,UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding ClearCommand}"
CommandParameter="{Binding ElementName=TextBoxB,Path=Text}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
You have to bind to relative source like this
<TextBox Name="TextBoxA"Text="{Binding myObject.TextPrp,UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type YourUserControl}, Path=DataContext. ClearCommand}"
CommandParameter="{Binding ElementName=TextBoxB,Path=Text}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>

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"/>

Should MouseRightButtonDown work with EventToCommand?

Is the RightMouseDownEvent expected to work with EventToCommand?
I can bind a Command with PreviewMouseRightUp and PreviewMouseRightDown, but MouseRightButtonDown does nothing. Am I using the wrong event name -- is it that simple?
In the xaml -- i have both events bound to the same handler -- only one works (Preview...Up). I comment out each in turn, but only the Preview works.
Thoughts? Thanks!
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding SetActivePaneCommand}" CommandParameter="{Binding PaneOne}" PassEventArgsToCommand="False" />
<cmd:EventToCommand Command="{Binding ListSelectionChangedCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="MouseRightButtonDown">
<cmd:EventToCommand Command="{Binding PreviewMouseRightButtonUpCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="PreviewMouseRightButtonUp">
<cmd:EventToCommand Command="{Binding PreviewMouseRightButtonUpCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
Some controls handle the MouseRightButtonDown event and doesn't let it propagate (e.g. MenuItem). You can test this easily if you're calling a code behind method instead of using a trigger and see if you'll reach a breackpoint in that method.
In your xaml:
MouseRightButtonDown="UIElement_OnMouseRightButtonDown"
In your code behind:
private void UIElement_OnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{ }

Bind an ICommand to a TabItem WPF (MVVM)

I have a TabControl with a few TabItems. I want one of my TabItems to act as a button. When I click on the TabItem, I want it to execute a Command in my associated ViewModel. I have the following code in my View:
<TabItem Header="Manage Users" Visibility="{Binding IsAdmin, Converter={StaticResource VisibilityOfBool}}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding Path=OpenLoginCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TabItem>
The OpenLoginCommand is an ICommand in the ViewModel. I have the interactivity namespace defined. What am I missing here?
Try PreviewMouseLeftButtonDown
<TabItem Header="Manage Users" Visibility="{Binding IsAdmin, Converter={StaticResource VisibilityOfBool}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding Path=OpenLoginCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TabItem>
Try using MouseDown instead of MouseLeftButtonDown as referencing MSDN the latter event doesn't exist on a TabItem control.
If your requirements insist on left-button only, then check the state of the mouse within the command.

Toggle ToolTip of a Button control using only XAML

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
}

Resources