Xamarin Forms WebView cache: GoBack() doesn't call Navigating event - c#

As I previously said in this question, I'm new at Xamarin.Forms and I'm developing a cross-platform web browser with Microsoft Visual Studio 2017 version 15.5.4. I'm debugging on an Android 5.1 smartphone.
In my layout there's a WebView and two Buttons to go back/forward.
<Button Image="backarrowdisabled.png"
Grid.Row="1"
Grid.Column="0"
x:Name="backButton"
IsEnabled="False"
Clicked="previousPage">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding CanGoBack, Source={Reference appWebView}}"
Value="False">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Image" Value="backarrowdisabled.png" />
</DataTrigger>
<DataTrigger TargetType="Button"
Binding="{Binding CanGoBack, Source={Reference appWebView}}"
Value="True">
<Setter Property="IsEnabled" Value="True" />
<Setter Property="Image" Value="backarrow.png" />
</DataTrigger>
</Button.Triggers>
</Button>
<Button Image="nextarrowdisabled.png"
Grid.Row="1"
Grid.Column="5"
x:Name="nextButton"
IsEnabled="False"
Clicked="nextPage">
/* triggers */
</Button>
<WebView Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="6"
x:Name="appWebView"
Source="https://www.google.it/"
Navigating="onPageLoading"
Navigated="onPageLoaded"/>
As you can see there are two methods to manage Navigating and Navigated events, which are
private void onPageLoading(object sender, WebNavigatingEventArgs e)
{
URLEntry.Text = e.Url; //Entry where I can see the page URL
//other code
}
private void onPageLoaded(object sender, EventArgs e)
{
//some code
}
When I click on a link or something like that in my WebView everything works correctly, but when I have to go back/forward in my history the Entry doesn't update the URL to the previous/next one. It seems it doesn't even call the onPageLoading method, but the WebView loads the page.
These are the methods called by the Buttons
private void previousPage (object sender, EventArgs e)
{
appWebView.GoBack();
}
private void nextPage (object sender, EventArgs e)
{
appWebView.GoForward();
}
Is it a WebView bug or am I doing something wrong?
Edit: I think the pages are cached, is there a way to make it not do it?

One thing you can do, is to create another event handler for the WebView's PropertyChanged or PropertyChanging event. There, you can check to see if the Source property was changed; If so, you can update the TextView with the new source. (I'm not sure exactly, but it seems like whe navigating back or forward, the Navigating event does not get called, possibly because the page is already cached?)
See here: https://developer.xamarin.com/api/member/Xamarin.Forms.WebView.OnPropertyChanged/p/System.String/
So your code can look like
<WebView x:Name="MyWebView" PropertyChanged="OnWebViewPropertyChanged" />
and then your code behind:
private void OnWebViewPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == WebView.SourceProperty.PropertyName)
{
URLEntry.Text = MyWebView.Source.ToString(); // May need to check this
}
}

Related

MouseLeftButtonUp does not fire for ScrollViewer in WPF control template

In WPF and C# I am trying to set up a mouse drag scrolling feature for a ScrollViewer contained within a control template for a document viewer. The problem: I have not been able to get the MouseLeftButtonEvent to fire.
It is basically the default DocumentViewer template, with a few features modified. Here is an outline in XAML:
<Style x:Key="DocumentViewerStyle1" BasedOn="{x:Null}" TargetType="{x:Type DocumentViewer}">
<!--...—>
<Setter Property="ContextMenu" Value="{x:Null}" /> <!--So does not mess up right click, if I use that-->
<!--...-->
<ControlTemplate TargetType="{x:Type DocumentViewer}">
<!--...-->
<ScrollViewer x:Name="PART_ContentHost" CanContentScroll="True"
IsHitTestVisible="True" HorizontalScrollBarVisibility="Auto"
Grid.Row="1" Loaded ="OnScrollViewerLoaded" />
<DockPanel Grid.Row="1" >
<!-...-->
</ControlTemplate>
</Style>
I use the following in code behind so that I can access the ScrollViewer. If one changes “Left” to “Right” in the method below, it works to perfection, but of course with the right mouse button rather than the left.
public partial class PrintPreview : Window
{
private ScrollViewer nomad;
etc. and
private void OnScrollViewerLoaded(object sender, RoutedEventArgs e)
{
nomad = (ScrollViewer)sender;
nomad.AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseButtonDown), true);
nomad.AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseButtonUp), true);
nomad.AddHandler(MouseMoveEvent, new MouseEventHandler(OnMouseMove), true);
}
The OnMouseButtonUp event handler, for example, is
private void OnMouseButtonUp(object sender, MouseButtonEventArgs e)
{
nomad.Cursor = Cursors.IBeam;
nomad.ReleaseMouseCapture();
}
Have tried various things found here: No help from using Preview events for my three mouse events. No help from setting Focusable="False" for the ScrollViewer, or for setting a Background for it. Any suggestions? Thanks!

change text color on disabled button (custom ButtonRenderer)

I use Xamarin.Forms, I create custom Renderer FlatButtonRenderer for Android. I want to add to it only one parametr - white text color on disabled button. What easy way to do it? On enabled button I simple can change Button.TextColor in PCL project. If way to change text color on disabled with c# code?
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms;
[assembly: ExportRenderer(typeof(Button), typeof(ugrid.Droid.FlatButtonRenderer))]
namespace myProject.Droid
{
public class FlatButtonRenderer : ButtonRenderer
{
protected override void OnDraw(Android.Graphics.Canvas canvas)
{
base.OnDraw(canvas);
}
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
}
}
}
This sounds like you would be better using a style with a trigger rather than a custom renderer. An example for a button changing background to red on disabled would look something like this:
<Button Command="{Binding Command}" Text="Blah Button">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger TargetType="Button" Property="IsEnabled" Value="False">
<Setter Property="BackgroundColor" Value="Red"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
This could also be done in the code behind if required. If the style is used elsewhere you would want to add it to the resources and reference it.
If you want to change the style just for the one platform, use the "OnPlatform"

IsManipulationEnabled = true prevents touch toggling a togglebutton

I am looking for a solution to making touch able to handle toggling a toggle button when IsManipulationEnabled = true. I have to keep on IsManipulationEnabled due to underlying 3d map.
Here is the test project I have been using.
<Window x:Class="TestingEventManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" IsManipulationEnabled="True">
<StackPanel HorizontalAlignment="Stretch">
<ToggleButton Height="40">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="OFF"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="ON"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<ComboBox SelectedIndex="0" Height="40">
<ComboBoxItem>Test 1</ComboBoxItem>
<ComboBoxItem>Test 2</ComboBoxItem>
</ComboBox>
</StackPanel>
I have looked into setting it in the app.xaml style, but setting it just for togglebutton seems to not extend down into the combobox style and it can be easily overridden by another style.
I also would not like to make a custom class because then everyone would need to remember to use this derived class.
Here is a msdn blog article that describes some of these issues with mixing touch
MSDN blog article
And here is an article with someone having a similar problem, but she just extended the button.
MSDN Social Article
Well last night I came up with somewhat of a solution that I am going to roll with in our code. It is not the best solution due to it always turning on ManipulationEnabled for every togglebutton and its manually handling the IsChecked now, but its the only thing I could come up with to always manipulate every togglebutton.
private void EnableTouchDownTogglingOfToggleButton()
{
EventManager.RegisterClassHandler( typeof( ToggleButton ), ToggleButton.LoadedEvent, new RoutedEventHandler( TurnOnManipulaitonEnabled ) );
EventManager.RegisterClassHandler( typeof( ToggleButton ), ToggleButton.TouchDownEvent, new RoutedEventHandler( EnableTouchDownTogglingHandler ) );
}
private void TurnOnManipulaitonEnabled( object sender, RoutedEventArgs e )
{
// need to make sure all toggle buttons behave the same so we always assume IsManipulationEnabled is true
// otherwise it can open then close right after due to it firing again from mousedown being able to go through
ToggleButton toggle = sender as ToggleButton;
if ( toggle != null )
toggle.IsManipulationEnabled = true;
}
private void EnableTouchDownTogglingHandler( object sender, RoutedEventArgs e )
{
ToggleButton toggle = sender as ToggleButton;
if ( toggle != null )
toggle.IsChecked ^= true;
}

Spellcheck only replaces first word in TextBox

I know I have seen this problem before somewhere, but I'm not sure if there was an answer at the time. I'm trying to add SpellCheck to a TextBox in WPF, .NET 4.0. It works fine in terms of finding and marking the incorrect words, and will replace the first word in the TextBox if it's incorrect. Anything past word one though, and it just moves the carat to the start of the TextBox without changing anything? As I said I saw this somewhere about 6-9 months ago, but now everything I come up with in google deals with alternate languages (I'm staying strictly in English for now). I've included the event methods and styling XAML only for completeness, I don't think the issue lies there.
XAML:
<MultiBox:MultiBox Name="callNotes" Grid.Column="1" Width="Auto" Height="Auto" Margin="2,5,15,20" VerticalAlignment="Stretch" AcceptsReturn="True" FontWeight="Bold" GotFocus="callNotes_GotFocus" SelectAllOnGotFocus="False" SpellCheck.IsEnabled="True" xml:lang="en-US" Style="{StaticResource TextBoxStyle}" TextChanged="callNotes_TextChanged" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" />
<Style x:Key="TextBoxStyle" TargetType="{x:Type MyNamespace:MultiBox}">
<Setter Property="CharacterCasing" Value="Upper" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Height" Value="23" />
<Setter Property="Width" Value="Auto" />
<Setter Property="SelectAllOnGotFocus" Value="True" />
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
Code:
private void callNotes_TextChanged(object sender, TextChangedEventArgs e)
{
callNotes.Text.ToUpper();
lineCountOne.Content = ((callNotes.Text.Length / 78) + 1);
}
private void callNotes_GotFocus(object sender, RoutedEventArgs e)
{
callNotes.CaretIndex = callNotes.Text.Length;
}
It would help to see your code which attempts to correct the errors. Here's simple code which loops through all the detected errors and accepts the first suggestion. If you only want to fix particular errors, you'll need to skip to the particular error you're interested in by getting the error at a certain index.
int ndx;
while ((ndx = callNotes.GetNextSpellingErrorCharacterIndex(0, LogicalDirection.Forward)) != -1)
{
var err = callNotes.GetSpellingError(ndx);
foreach (String sugg in err.Suggestions)
{
err.Correct(sugg);
break;
}
}
After trying jschroedl's suggestion and still having no luck (although I do know that his answer should have been correct), I started playing with every possible setting I could think of, even to the point of creating a completely new WPF project with a single, Spellcheck-enabled TextBox just to make sure it wasn't something with the Visual Studio/.NET installation itself. Turns out it wasn't, it was something I had done months ago to ensure that selecting any given TextBox through the program would result in the SelectAll() method being triggered. Once I screened out this particular TextBox from that bit of code, all works great. Again, thanks to jschroedl, I know there is no way he could have known this. The offending code is below, in case anybody comes across a similar issue.
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(TextBox), UIElement.GotKeyboardFocusEvent, new RoutedEventHandler(SelectAllText), true);
base.OnStartup(e);
}
protected static void SelectAllText(object sender, RoutedEventArgs e)
{
var textBox = e.OriginalSource as TextBox;
if (textBox != null && textBox.Name != "callNotes")
textBox.SelectAll();
}
Adding the && textBox.Name != "callNotes" solved the problem.

How to call method in a different namespace from XAML

I am building a desktop application with WPF and want to open a hyperlink in a browser. I can do this by putting a method in the code behind and calling it from the XAML as follows, but how can I call this method from multiple XAML pages?
XAML
<Hyperlink NavigateUri="http://www.mylink.com" RequestNavigate="Hyperlink_RequestNavigate">My link text</Hyperlink>
C#
private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
You could put this into a style in App.xaml, e.g.
<Application.Resources>
<Style x:Key="LaunchLinkStyle" TargetType="{x:Type Hyperlink}">
<EventSetter Event="RequestNavigate" Handler="LaunchLinkStyle_RequestNavigate" />
</Style>
</Application.Resources>
(The handler then of course would be implemented in App.xaml.cs)
You then can just reference the style:
<Hyperlink Style="{StaticResource LaunchLinkStyle}" ... />
Thanks H.B. Your answer set me on the right path. Here's the complete code:
In my page:
<Hyperlink NavigateUri="http://www.mylink.com" Style="{StaticResource LaunchLinkStyle}">My Link</Hyperlink>
App.xaml
<Style x:Key="LaunchLinkStyle" TargetType="{x:Type Hyperlink}">
<EventSetter Event="RequestNavigate" Handler="LaunchLinkStyle_RequestNavigate"/>
</Style>
App.xaml.cs
public void LaunchLinkStyle_RequestNavigate(object sender, RoutedEventArgs e)
{
/* Function loads URL in separate browser window. */
Hyperlink link = e.OriginalSource as Hyperlink;
Process.Start(link.NavigateUri.AbsoluteUri);
e.Handled = true; //Set this to true or the hyperlink loads in application and browser windows
}

Categories

Resources