UWP MVVM NavigationView BackButton - Detect when it is pressed - c#

This question is in the context of using MVVM pattern. I have a NavigationView in my XAML but I have no way to detect when the user clicks on the back button. I could do it in the code behind file easily but that doesnt seems like what you want to do in an MVVM context. So is there a way to detect when the user presses the back button from a NavigationView outside of the code behind cs file? I would attach a Command Object to it and use my NavigationService where I will be able to switch the Frame. So far I use the Behaviors NuGet package to detect the clicks on the MenuItems but it doesnt work for the BackButton. Here is the Xaml so far:
<Page
x:Class="ToolBoxApp.Views.AudioHomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ToolBoxApp.Views"
xmlns:viewmodels="using:ToolBoxApp.ViewModels"
xmlns:mainview="clr-namespace:ToolBoxApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<NavigationView x:Name="navigationViewControl"
IsBackEnabled="true">
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ItemInvoked">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{Binding NavigateToView}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
<NavigationView.MenuItems>
<NavigationViewItem Icon="MusicInfo" Content="Text to Speech"/>
<NavigationViewItem Icon="MusicInfo" Content="Youtube to Mp3"/>
</NavigationView.MenuItems>
<ScrollViewer>
<Frame SourcePageType="{Binding ScrollAudioView, Mode=TwoWay}"/>
</ScrollViewer>
</NavigationView>
</Grid>
</Page>

Why don't you simply add another EventTriggerBehavior that invokes another command for the BackRequested event?:
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="BackRequested">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{Binding BackCommand}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="ItemInvoked">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{Binding NavigateToView}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>

Related

WPF ItemsPanel with Caliburn.Micro MVVM

I'm developing a WPF application and using Caliburn.Micro for MVVM. I'need to dynamically add buttons into view during the runtime.
I have done some research and I got to know that I can achieve this using an ItemControl.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ItemsControl>
<Button x:Name="ExecuteMethod1" Content="Execute1" />
<Button x:Name="ExecuteMethod2" Content="Execute2" />
<Button x:Name="ExecuteMethod3" Content="Execute3" />
</ItemsControl>
</Grid>
</Window>
Caliburn.Micro's convention allows to call a method which matches to Button's x:Name property. In above code button named ExecuteMethod1 will call a method ExecuteMethod1() in ViewModel.
Problem:
As I'm adding buttons dynamically how can I set the X:Name property for buttons?
I tried to set it using Binding, but WPF doesn't allow to bind X:Name property.
Is there any alternatives? Could anyone can help me with a suggestion?
I am not sure if this a new feature. Got it working from the Caliburn Micro Documentation.
Add Windows Interactivity Assembly. You can pass to your action the Name or the Content of the Button that shall be enough to identify which button was clicked and perform the right action.
CaliburnMicro Actions
<UserControl x:Class="Caliburn.Micro.HelloParameters.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cal="http://www.caliburnproject.org">
<StackPanel>
<TextBox x:Name="Name" />
<Button Content="Click Me">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="SayHello">
<cal:Parameter Value="{Binding ElementName=Name, Path=Text}" />
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
</UserControl>

Set TargetObject in CallMethodAction as ViewModel from parent DataContext in XAML

I'm creating a UWP app and so far I have been using the CallMethodAction to call methods from the ViewModel and it worked fine.
But now, I'm trying to use the same method for some buttons inside a ListView, and nothing happens when I click the button. The XAML code is:
<Page.DataContext>
<vm:RoomPageViewModel x:Name="ViewModel" />
</Page.DataContext>
<ListView x:Name="ActuatorListView"
ItemsSource="{x:Bind ViewModel.Room.Actuators}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Actuator">
<Button x:Name="OnButton" Content="On">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior
EventName="Click"
SourceObject="{Binding ElementName=OnButton}">
<Core:CallMethodAction
MethodName="OnButton_Click"
TargetObject="{Binding ElementName=ViewModel, Mode=OneWay}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have tried several ways to access the parent DataContext, but I found none working.
Because your view model is actually not an element, you can't use it with ElementName for binding.
The right solution in your case would be to give your page a name (x:Name="Page") and bind to its DataContext:
<Core:CallMethodAction
MethodName="OnButton_Click"
TargetObject="{Binding Path=DataContext, ElementName=Page, Mode=OneWay}" />

Set focus of textbox in context menu - wpf

I've looked into several methods of setting focus and nothing appeared to work. I'm sure someone out there has a solution to this. It's such a simple task.
I want to set the focus of the textbox which appears in the context menu when the user right-clicks on the listbox. I don't want the user to have to click the textbox each time they right-click.
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox>
<ListBox.ContextMenu>
<ContextMenu>
<ContextMenu.Template>
<ControlTemplate>
<Border BorderThickness="2" BorderBrush="sc#1,.1,.1,.1" CornerRadius="4"
Background="sc#1,.05,.05,.05">
<TextBox Grid.Row="0" Margin="4" MinWidth="150" Name="SearchBox" VerticalAlignment="Center">
</TextBox>
</Border>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</Grid>
</Window>
IsFocused property of TextBox is read only. This forces the use of method in our case.
You need CallMethodAction behavior. Good tutorial to start with.
<TextBox Grid.Row="0" Margin="4" MinWidth="150" Name="SearchBox" VerticalAlignment="Center">
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger Binding="{Binding IsOpen, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}">
<ei:CallMethodAction MethodName="FocusSearchBox" TargetObject="{Binding DataContext, ElementName=SearchBox}"/>
<ei:ChangePropertyAction PropertyName="Background" Value="Purple"/>
</ei:PropertyChangedTrigger>
</i:Interaction.Triggers>
</TextBox>
public void FocusSearchBox()
{
TextBox t = (TextBox) CtxMenu.ContextMenu.Template.FindName("SearchBox", CtxMenu.ContextMenu);
t.Focus();
}

UWP CommandBar - Content is completely separate context?

I've been trying to implement Microsoft's hamburger pattern in my own way for some time.
Final result was the following:
AppShell for containing current page and hamburger menu (menu always visible)
In Appshell, Grid with two rows: one 48px in height, one one-start height.
In first row, added a commandbar (global), and hamburger button (togglebutton with custom style for being 48px wide and tall, with the hamburger fonticon in the center)
In second row, SplitView with a ListBox on the Pane, and a Frame in the Content.
This way one has control over the content, while displaying a global menu and command bar. On the Navigated event of the Frame, I update the CommandBar to pull the primary and secondary commands from the Frame's Content's properties (I use a custom page control with those properties), and the content of the CommandBar (which is, of now, a single TextBlock with binding).
However, I wanted to move the ToggleButton into the CommandBar. It worked nicely, except the binding (IsChecked of ToggleButton is bound to IsPaneOpen of SplitView) does not work. I use the regular ElementName targeting, and would prefer to not to use a ViewModel property.
Does the CommandBar.Content use a different context? Or why doesn't the ElementName reference work?
Only way, how I was able to bind to AppBarToggleButton was to set the DataContext in the code behind.
XAML:
<Page.BottomAppBar>
<CommandBar x:Name="CommandBar">
<AppBarToggleButton x:Name="Button" IsChecked="{Binding IsPaneOpen, Mode=TwoWay}" Icon="Home" Label="Button"/>
</CommandBar>
</Page.BottomAppBar>
Code behind:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
Button.DataContext = SplitView;
}
This works for me:
<Page
x:Class="StackOverflowTests.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="48"/>
<RowDefinition/>
</Grid.RowDefinitions>
<CommandBar>
<CommandBar.Content>
<AppBarToggleButton Icon="Globe" IsChecked="{Binding ElementName=SplitView,Path=IsPaneOpen,Mode=TwoWay}"/>
</CommandBar.Content>
</CommandBar>
<SplitView Grid.Row="1" Name="SplitView" IsPaneOpen="{x:Bind Appbarbutton.IsChecked.Value,Mode=OneWay}">
<SplitView.Pane>
<ListBox>
<ListBoxItem>Foo</ListBoxItem>
<ListBoxItem>Bar</ListBoxItem>
</ListBox>
</SplitView.Pane>
<SplitView.Content>
<TextBlock>stuff here</TextBlock>
</SplitView.Content>
</SplitView>
</Grid>
<Page.BottomAppBar>
<CommandBar>
<CommandBar.Content>
<AppBarToggleButton Name="Appbarbutton" Icon="Home" />
</CommandBar.Content>
</CommandBar>
</Page.BottomAppBar>
</Page>
You have bind DataContext:
Example:
<Page.BottomAppBar >
<CommandBar x:Name="commandBar" DataContext="{Binding}">
<CommandBar.Content>
<StackPanel Margin="0,0">
<ListView ItemsSource="{Binding Confirmation.AvailableValues}" ItemContainerStyle="{StaticResource ListViewItemBase}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Value.DisplayName}" Command="{Binding DataContext.Confirm, ElementName=commandBar}" CommandParameter="{Binding Value}" HorizontalAlignment="Center"></Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Line Margin="5" X1="0" X2="1000" Y1="0" Y2="0" HorizontalAlignment="Stretch" Height="2" Stroke="#888888" StrokeThickness="1" ></Line>
</StackPanel>
</CommandBar.Content>
</CommandBar>
</Page.BottomAppBar>

WPF KeyTrigger not triggered when KeyBinding exists

In my WPF application, I have a TextBox that has a KeyBinding and KeyTrigger on it, but it seems that the KeyTrigger is not invoked.
MainWindow.xaml
<Window
x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Title="MainWindow" Height="350" Width="525">
<TextBox>
<TextBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding LogCommand}" CommandParameter="from KeyBinding"/>
</TextBox.InputBindings>
<i:Interaction.Triggers>
<ei:KeyTrigger Key="Return">
...
</ei:KeyTrigger>
</i:Interaction.Triggers>
</TextBox>
</Window>
Is there any way to invoke both of them? I want to do this because there is something I don't want to do in ViewModel.
Just put both in the interaction.Triggers part and remove the direct input bindings:
<TextBox>
<i:Interaction.Triggers>
<ei:KeyTrigger Key="Return">
<i:InvokeCommandAction Command="{Binding LogCommand}" CommandParameter="from KeyBinding"/>
...
</ei:KeyTrigger>
</i:Interaction.Triggers>
</TextBox>

Categories

Resources