C# WinUI 3 frame navigation stack - c#

In my WinUI3 C# application I have a frame with a content page. Given that I set IsNavigationStackEnabled to true on the frame, I'd expect the current page to be pushed to the navigation stack when I navigate (using MyFrame.Navigate(...)) so I could use MyFrame.GoBack() to navigate back to the previous page.
However, trying to invoke MyFrame.GoBack() after navigating from one page to another always results in an exception: System.Runtime.InteropServices.COMException: 'Error HRESULT E_FAIL has been returned from a call to a COM component.'. What's more, the CanGoBack-property is always false.
Am I missing something or is the navigation stack just not managed by the frame in WinUI3 and do I need to do this manually?

This a working sample code for Frame navigation. Note that there's no IsNavigationStackEnabled in the code. IsNavigationStackEnabled is true by default.
MainWindow.xaml
<Grid ColumnDefinitions="*,*">
<StackPanel
Grid.Column="0"
Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button
x:Name="Page1Button"
Click="Page1Button_Click"
Content="Page1" />
<Button
x:Name="Page2Button"
Click="Page2Button_Click"
Content="Page2" />
<Button
x:Name="Page3Button"
Click="Page3Button_Click"
Content="Page3" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button
x:Name="BackButton"
Click="BackButton_Click"
Content="Back" />
<Button
x:Name="NextButton"
Click="NextButton_Click"
Content="Next" />
</StackPanel>
<Frame x:Name="NavigationFrame" />
</StackPanel>
<ListView Grid.Column="1" ItemsSource="{x:Bind NavigationLogs}" />
</Grid>
MainWindow.xaml.cs
using Microsoft.UI.Xaml;
using System;
using System.Collections.ObjectModel;
namespace Frames;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
NavigateTo(typeof(Page1));
}
private ObservableCollection<string> NavigationLogs { get; } = new();
private void BackButton_Click(object sender, RoutedEventArgs e)
{
NavigateBack();
}
private void NextButton_Click(object sender, RoutedEventArgs e)
{
NavigateForward();
}
private void Page1Button_Click(object sender, RoutedEventArgs e)
{
NavigateTo(typeof(Page1));
}
private void Page2Button_Click(object sender, RoutedEventArgs e)
{
NavigateTo(typeof(Page2));
}
private void Page3Button_Click(object sender, RoutedEventArgs e)
{
NavigateTo(typeof(Page3));
}
private void NavigateTo(Type pageType)
{
this.NavigationFrame.Navigate(pageType);
NavigationLogs.Add($"Navigated to {pageType}.");
}
private void NavigateBack()
{
if (this.NavigationFrame.CanGoBack is true)
{
this.NavigationFrame.GoBack();
NavigationLogs.Add("Navigated back.");
}
else
{
NavigationLogs.Add("Cannot to navigate back.");
}
}
private void NavigateForward()
{
if (this.NavigationFrame.CanGoForward is true)
{
this.NavigationFrame.GoForward();
NavigationLogs.Add("Navigated forward.");
}
else
{
NavigationLogs.Add("Cannot to navigate forward.");
}
}
}

Related

How to use the click method on a button in WPF? CS8321: The local function is declared but never used

In my XAML code I have the following code:
<Button Name="btnOpenSupplyLine" HorizontalAlignment="Left" Margin="50,75,0,0" VerticalAlignment="Top" Click="OpenSupplyLine" ClickMode="Release" Background="White" BorderThickness="0">
<Button.Content>
<Rectangle Height="200" Stroke="Black" Width="200" >
</Rectangle>
</Button.Content>
</Button>
Now when I use this code in my .cs file:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Console.WriteLine(System.AppDomain.CurrentDomain.BaseDirectory);
void OpenSupplyLine(object sender, RoutedEventArgs e)
{
btnOpenSupplyLine.Background = Brushes.Red;
}
}
}
I get the following error message: CS8321: The local function OpenSupplyLine is declared but never used.
What's the proper way of doing a click event on WPF?
As you can see, you wrote the handler inside the constructor, as a local function.
Move it out of the constructor.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Console.WriteLine(System.AppDomain.CurrentDomain.BaseDirectory);
}
private void OpenSupplyLine(object sender, RoutedEventArgs e)
{
btnOpenSupplyLine.Background = Brushes.Red;
}
}

How to access CommandBar controls from Frame

I'm trying to dynamically access my CommandBar from frames to control its back button. How can I ensure the CommandBar back button is hidden on the first frame (Frame1) whilst being visible and clickable on the second frame (Frame2)?
MainPage.xaml
<Page>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<CommandBar>
<CommandBar.Content>
<Button
Click="Back_Click"
x:FieldModifier="public"
Style="{StaticResource NavigationBackButtonNormalStyle}"
Name="BackButton"
VerticalAlignment="Top" />
</CommandBar.Content>
</CommandBar>
<Frame Name="MyFrame"/>
</Grid>
</Page>
MainPage.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
Current = this;
Frame_Main.Navigate(typeof(Frame1));
}
public static MainPage Current;
private void Back_Click(object sender, RoutedEventArgs e)
{
On_BackRequested();
}
private bool On_BackRequested()
{
if (this.Frame.CanGoBack)
{
this.Frame.GoBack();
return true;
}
return false;
}
private void BackInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
On_BackRequested();
args.Handled = true;
}
}
Frame1.cs
public sealed partial class Frame1: Page
{
public Frame1()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
MainPage.Current.BackButton.Visibility = Visibility.Collapsed;
}
}
Frame2.cs
public sealed partial class Frame2: Page
{
public Frame2()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
MainPage.Current.BackButton.Visibility = Visibility.Visible;
MainPage.Current.BackButton.IsEnabled = true;
}
}
If you want to access the Back Button from Frame1 and Frame2, you could try to set the x:FieldModifier of BackButton as public. In this case, it will be public and you can access the button by its x:name from other pages. In addition, it's better to put the click event in the MainPage instead of Frame2. For example:
MainPage.xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<CommandBar>
<CommandBar.Content>
<Button x:FieldModifier="public" Click="Back_Click" Name="BackButton" Style="{StaticResource NavigationBackButtonNormalStyle}" VerticalAlignment="Top"/>
</CommandBar.Content>
</CommandBar>
<Frame Name="MyFrame" Grid.Row="1"/>
</Grid>
MainPage.xaml.cs:
You need to define a public static MainPage instance to let other pages access your button through this instance.
public MainPage()
{
this.InitializeComponent();
Current = this;
MyFrame.Navigate(typeof(Frame1));
}
public static MainPage Current;
private void Back_Click(object sender, RoutedEventArgs e)
{
On_BackRequested();
}
private bool On_BackRequested()
{
if (MyFrame.CanGoBack)
{
MyFrame.GoBack();
return true;
}
return false;
}
private void BackInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
On_BackRequested();
args.Handled = true;
}
Frame1.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
MainPage.Current.BackButton.Visibility = Visibility.Collapsed;
}
Frame2.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
MainPage.Current.BackButton.Visibility = Visibility.Visible;
MainPage.Current.BackButton.IsEnabled = true;
}
Or you can define a property to bind with the Visibility of Button, when you navigate to next page, you can pass the property in the Navigate method(e.g. Frame_Main.Navigate(typeof(Frame1), VM);) and then in the OnNavigatedTo event to change its value.

WPF: How do I use one User control for multiple different Click events

I'm trying to create a User control for all the buttons on my homepage, every user control should have a different Click event. I'm trying to solve this by adding a property to the User control (which works for the label and image) but i can't find any solution for the Click event.
ImageLabelButton.xaml:
<UserControl x:Class="SC.UI.WPF.Controls.ImageLabelButton"
<Grid>
<Button Name="BtnClick">
<StackPanel>
<Image Name="ImageButton"/>
<Label Name="LabelButton"/>
</StackPanel>
</Button>
</Grid>
ImageLabelButton.xaml.cs:
....
<!-- works -->
public string Name
{
get { return this.LabelButton.Content.ToString(); }
set { this.LabelButton.Content = value; }
}
<!-- works -->
public ImageSource SetSource
{
get { return ImageButton.Source; }
set { ImageButton.Source = value; }
}
<!-- doesn't work -->
public EventHandler ButtonAction
{
get { return BtnClick.Click; }
set { BtnClick.Click= value; }
}
Implementation.xaml:
....
<controls:ImageLabelButton Name="first" SetSource="test.png" ButtonAction="Click1"/>
<controls:ImageLabelButton Name="first" SetSource="test.png" ButtonAction="Click2"/>
<controls:ImageLabelButton Name="first" SetSource="test.png" ButtonAction="Click3"/>
You just need to set the event handler up as a property of the user control and set it in the usage code:
User control XAML:
<Grid>
<Button Name="BtnClick" Click="BtnClick_DoClick">
<StackPanel>
<Image Name="ImageButton"/>
<Label Name="LabelButton"/>
</StackPanel>
</Button>
</Grid>
User control codebehind:
public EventHandler<RoutedEventArgs> ButtonAction;
private void BtnClick_DoClick(object sender, RoutedEventArgs e)
{
if (ButtonAction != null) ButtonAction(sender, e);
}
Implementation XAML:
<controls:ImageLabelButton Name="first1" SetSource="test.png" />
<controls:ImageLabelButton Name="first2" SetSource="test.png" />
<controls:ImageLabelButton Name="first3" SetSource="test.png" />
Implementation code behind:
public MainWindow()
{
InitializeComponent();
first1.ButtonAction = Button1;
first2.ButtonAction = Button2;
first3.ButtonAction = Button3;
}
private void Button3(object sender, RoutedEventArgs e) { MessageBox.Show("Pressed 3"); }
private void Button2(object sender, RoutedEventArgs e) { MessageBox.Show("Pressed 2"); }
private void Button1(object sender, RoutedEventArgs e) { MessageBox.Show("Pressed 1"); }
Although you can't set the event handling in XAML this way.
You need a RoutedEvent on your user control that can be accessed from Implementation.xaml
ImageLabelButton.xaml
<Button Name="BtnClick" Click="Submit_Click">
<StackPanel>
<Image Name="ImageButton"/>
<Label Name="LabelButton"/>
</StackPanel>
</Button>
ImageLabelButton.cs
private void Submit_Click(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(ClickEvent, this));
}
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
"Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyUserControl));
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
Implementation.xaml
<controls:ImageLabelButton Name="first" SetSource="test.png" Click="Click1"/>
Implementation.xaml.cs
private void Click1(object sender, RoutedEventArgs e)
{
}

How to show progressbar until WP8 WebBrowser control loaded parameter?

I have a WebBrowser control and I want to show some url by parameter on this control. Until the webbrower loaded the page, I need to show some progressbar or animation.
Please help me, here's what I have:
public partial class brw : PhoneApplicationPage
{
public brw()
{
InitializeComponent();
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string parameterValue = NavigationContext.QueryString["parameter"];
System.Uri uri = new System.Uri(parameterValue);
webbrowser.Source = uri;
}
private void WebBrowser_Navigating_1(object sender, Microsoft.Phone.Controls.NavigatingEventArgs e)
{
progressbar.Visibility = Visibility.Visible;
}
private void WebBrowser_Navigated_1(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
progressbar.Visibility = Visibility.Collapsed;
}
private void PhoneApplicationPage_Loaded_1(object sender, System.Windows.RoutedEventArgs e)
{
}
}
Thank you
This can be achieved by using LoadCompleted property.
XAML:
<ProgressBar x:Name="progressbar" IsIndeterminate="True"/>
<phone:WebBrowser x:Name="webbrw" IsScriptEnabled="True" LoadCompleted="yesLoaded"/>
.cs:
private void yesLoaded(object sender, NavigationEventArgs e)
{
this.progressbar.Visibility = Visibility.Collapsed;
this.progressbar.IsIndeterminate = False;
}
Have a look at this sample.
Hope it helps!
try this:
webbrowser.Navigate(new Uri(parameterValue));
instead of
webbrowser.Source = uri;
set Progressbar's Property IsIndeterminate="True"
<ProgressBar Foreground="Orange" x:Name="progressbar" Visibility="Collapsed" IsIndeterminate="True" Height="4" HorizontalAlignment="Left" Margin="10,66,0,0" VerticalAlignment="Top" Width="460" />

Execute command does not fire in resource dictionary code behind

I have created resource dictionary and code behind file for it.
In XAML I have defined command binding and added Executed handler:
<Button Grid.Row="2" Width="100" >
<CommandBinding Command="Search" Executed="CommandBinding_Executed" />
</Button>
Here is code behind:
partial class StyleResources : ResourceDictionary {
public StyleResources() {
InitializeComponent();
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) {
//this is never executed
}
}
I don't know why is command not executing when button is clicked, and also, why is button enabled when I didn't set CanExecute to true. I have also tried to set it to true, but CanExecute event didn't fire as well.
Here is how I am using the resource dictionary:
public partial class MyWindow : Window {
public MyWindow() {
InitializeComponent();
Uri uri = new Uri("/WPFLibs;component/Resources/StyleResources.xaml", UriKind.Relative);
ResourceDictionary Dict = Application.LoadComponent(uri) as ResourceDictionary;
this.Style = Dict["WindowTemplate"] as Style;
}
}
This is not how you bind commands to buttons. It should look something like this:
<Grid>
<Grid.CommandBindings>
<CommandBinding Command="Search"
Executed="Search_Executed"
CanExecute="Search_CanExecute" />
</Grid.CommandBindings>
...
<Button Grid.Row="2" Width="100" Command="Search" />
...
</Grid>
And in codebehind:
private void Search_Executed(object sender, ExecutedRoutedEventArgs e) {
// do something
}
private void Search_CanExecute(object sender, CanExecuteRoutedEventArgs e) {
e.CanExecute = ...; // set to true or false
}

Categories