View a pdf with Webview Xamarin Android - c#

I would like to use a webview to view a pdf but something must be missing.
Custom Webview code:
class CustomWebView : WebView
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(nameof(Uri), typeof(string), typeof(CustomWebView), default(string));
public string Uri {
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
CustomWebViewRenderer code:
class CustomWebViewRenderer : WebViewRenderer
{
public CustomWebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var customWebView = Element as CustomWebView;
Control.Settings.AllowUniversalAccessFromFileURLs = true;
Control.LoadUrl(string.Format("file:///android_asset/pdfjs/web/viewer.html?file={0}", string.Format("file:///android_asset/Content/{0}", WebUtility.UrlEncode(customWebView.Uri))));
}
}
}
xaml code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button HeightRequest="100"
WidthRequest="200"
HorizontalOptions="Center"
VerticalOptions="Center"
Clicked="Button_Clicked" Grid.Row="0"/>
<!--<local:CustomWebView Grid.Row="1" Uri="test.pdf" x:Name="PdfViewer" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"/>-->
<local:CustomWebView Grid.Row="1" x:Name="PdfViewer" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"/>
mainpage.xaml.cs:
private void Button_Clicked(object sender, EventArgs e)
{
PdfViewer.Uri = "test.pdf";
}
If I set uri from xaml the pdf is displayed but if I set it from the code behind it doesn't work.
How can I set the pdf from the code behind? Using debugging when I set the pdf from the code behind does not occur CustomWebViewRenderer.OnElementChanged and therefore does not update, how can I do?
Thanks in advance.

Related

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.

Error in method providing navigation between pages

I am working on an app that I had originally written all in C# (including UI) but now I am rewriting it so that UI is handled by xml. Every part of the app worked fine when it was all in C#, however, now that I have switched UI over to xml, I am getting an error in my ViewModels.
error: 'StartPageViewModel' does not contain a definition for 'Navigation' and no accessible extension method 'Navigation' accepting a first argument of type 'StartPageViewModel' could be found (are you missing a using directive or an assembly reference?)
XML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="SampleApp.Views.StartPage">
<ContentPage.Content>
<StackLayout>
<Grid VerticalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="OnePlayerGame" Text="One Player Game"
Command="{Binding StartOnePlayerGameCommand}"
Grid.Row="0" Grid.Column="0"
HorizontalOptions="Center"/>
<Button x:Name="TwoPlayerGame" Text="Two Player Game"
Command="{Binding StartTwoPlayerGameCommand}"
Grid.Row="0" Grid.Column="1" HorizontalOptions="Center" />
</Grid>
</StackLayout>
</ContentPage.Content>
Code behind
namespace SampleApp.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class StartPage : ContentPage
{
public StartPage()
{
InitializeComponent();
BindingContext = new StartPageViewModel();
}
}
}
ViewModel code
namespace SampleApp.ViewModels
{
class StartPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Command StartOnePlayerGameCommand { get; set; }
public Command StartTwoPlayerGameCommand { get; set; }
public StartPageViewModel()
{
StartOnePlayerGameCommand = new Command(() => StartOnePlayerGame());
StartTwoPlayerGameCommand = new Command(() => StartTwoPlayerGame());
}
private void StartOnePlayerGame()
{
//the error is here (Navigation has a red squigly under it)
this.Navigation.PushAsync(new Views.OnePlayerPage());
}
private void StartTwoPlayerGame()
{
//the error is here (Navigation has a red squigly under it)
this.Navigation.PushAsync(new Views.NameEntryPage());
}
}
}
Navigation is a property of Page and you can pass the Navigation through the ViewModel Constructor, then use it in the ViewModel:
class StartPageViewModel : INotifyPropertyChanged
{
public INavigation myNavigation { get; set; }
public StartPageViewModel(INavigation navigation)
{
myNavigation = navigation;
StartOnePlayerGameCommand = new Command(() => StartOnePlayerGame());
StartTwoPlayerGameCommand = new Command(() => StartTwoPlayerGame());
}
private void StartOnePlayerGame()
{
//the error is here (Navigation has a red squigly under it)
myNavigation.PushAsync(new Views.OnePlayerPage());
}
private void StartTwoPlayerGame()
{
//the error is here (Navigation has a red squigly under it)
myNavigation.PushAsync(new Views.NameEntryPage());
}
}
Pass it from Page:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = new StartPageViewModel(Navigation);
}
}
Another way it that you can try to use Application.Current.MainPage.Navigation:
private async void StartOnePlayerGame()
{
//the error is here (Navigation has a red squigly under it)
await Application.Current.MainPage.Navigation.PushAsync(new Views.OnePlayerPage());
}

How to bind boolean value to label

I am new to WPF. My code is as follows:
In my MainWindow.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Label HorizontalAlignment="Left" Margin="63,30,0,0" Grid.Row="0" VerticalAlignment="Top" Content="{Binding myVal}" Height="39" Width="71"/>
<Button Grid.Row="1" x:Name="btnSelect" Content="Select" Click="btnSelect_Click_1" Margin="396,0,10,0"/>
</Grid>
and MainWindow.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
private bool _myboolVal;
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void btnSelect_Click_1(object sender, RoutedEventArgs e)
{
if (myVal== false)
{
myVal = true;
}
else
{
myVal= true;
}
}
public bool myVal
{
get { return _myboolVal; }
set { _myboolVal= value; OnPropertyChanged("myVal"); }
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
But the value of the label is always false.
Your logic in btnSelect_Click_1 is incorrect. Update it to:
private void btnSelect_Click_1(object sender, RoutedEventArgs e)
{
myVal = !myVal;
}
take a look at this sample (MVVM, Command binding, MVVMLight)
Please set the Mode to TwoWay ,then it will works.
Content="{Binding myVal,Mode=TwoWay}"

Freezing main window after adding some items from childwindow

I am working on Silverlight 4 with mvvm and WCF services.
Whenever I am adding items from child window to main window. But at the same time main window automatically going to disabling mode. I think main window automatically freezes.
ChildWindow ViewModel
public class AddFormFieldInformationViewModel : ViewModelBase
{
private FieldInformationViewModel _FieldInformationViewModel;
public FieldInformationViewModel FieldInformationViewModel
{
get { return _FieldInformationViewModel; }
set
{
_FieldInformationViewModel = value;
RaisePropertyChanged("FieldInformationViewModel");
}
}
public void MoveSave(object obj)
{ this.FieldInformationViewModel.SelectedFormFields = FieldInformationModel;
ResultHandler(true);
}
public Action ResultHandler { get; set; }
}
ChildWindow .xaml.cs file
public partial class AddExistingFormFieldCategoryView : ChildWindow
{
private AddFormFieldInformationViewModel vm;
public AddExistingFormFieldCategoryView()
{
InitializeComponent();
vm = new AddFormFieldInformationViewModel();
this.DataContext = vm;
vm.ResultHandler = result => { if (result) { Close(); } };
}
}
Main Window ViewModel
public class FieldInformationViewModel : ViewModelBase
{ private void executeOpenChildWindow(object parameter)
{
AddExistingFormFieldCategoryView cw = new AddExistingFormFieldCategoryView();
((AddFormFieldInformationViewModel)cw.DataContext).FieldInformationViewModel = this;
cw.Show();
}
}
After adding items from child window into main window, sometimes my main window is automatically freezing.
Hi this is a bug you are experiencing see the sample below. I the same issue and you have to manually enable the display of the main page:
private static void ShowError(string message, string details)
{
ErrorWindow error = new ErrorWindow(message, details);
error.Closed += new EventHandler(error_Closed);
error.Show();
}
static void error_Closed(object sender, EventArgs e)
{
Application.Current.RootVisual.SetValue(Control.IsEnabledProperty, true);
}
You can subclass the ChildWindow and explicitly set the RootVisual to enabled. Source
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace DST_Common_Silverlight_Controls
{
/// <summary>
/// Bug in ChildWindow sometimes leaves app disabled.
/// </summary>
public class ChildWindowEx : ChildWindow
{
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Application.Current.RootVisual.SetValue(Control.IsEnabledProperty, true);
}
}
}
Then just use the new type instead of ChildWindow in xaml like this:
<slcommon:ChildWindowEx
x:Class="DST.AvSyncMonitor.Silverlight.Gui.ErrorWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:slcommon="clr-namespace:DST_Common_Silverlight_Controls;assembly=DST.Common.Silverlight.Controls"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
Title="Error">
<Grid x:Name="LayoutRoot" Width="540">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="IntroductoryText" Grid.Row="0" Margin="0"
Text="An unknown error was encountered. Please contact your administrator for more information."/>
<StackPanel x:Name="ContentStackPanel" Grid.Row="2" Margin="0,6,0,0">
<TextBlock x:Name="LabelText" TextWrapping="Wrap" Margin="0,0,0,2"
Text="Error details"/>
<TextBox x:Name="ErrorTextBox" Height="90" TextWrapping="Wrap" IsReadOnly="True"
VerticalScrollBarVisibility="Auto"/>
</StackPanel>
<Button x:Name="OKButton" Grid.Row="3" Click="OKButton_Click"
Width="75" Height="23" HorizontalAlignment="Right" Margin="0,10,0,0"
TabIndex="0" Content="OK"/>
</Grid>
</slcommon:ChildWindowEx>

Is there some way I can use assign a value in MainPage and bind it in the child window

I've a Silverlight application wherein I've a MainPage in which I need to assign a variable Name in the childwindow and assign it without using the object of the child. I need to bind thise value to a textbox in the Childwindow through XAML. How can it be done?
So far what I've done is using a dependancy property in the childwindow:
nameProp = DependencyProperty.Register("strName", typeof(string), typeof(TestWindow), new PropertyMetadata(null, new PropertyChangedCallback(OnNameChange)));
static TestWindow()
{
nameProp = DependencyProperty.Register("strName", typeof(string), typeof(TestWindow), new PropertyMetadata(null, new PropertyChangedCallback(OnNameChange)));
}
private static void OnNameChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetValue(nameProp, e.NewValue);
}
public string strName
{
get {
return (string)GetValue(nameProp);
}
set {
SetValue(nameProp, value);
}
}
and in TestWindow XAML i try to bind it:
<TextBox Text="{Binding Path=strName}" Height="23" HorizontalAlignment="Left" Margin="126,84,0,0" Name="txtName" VerticalAlignment="Top" Width="120"/>
How can I set the value for this dp from MainPage. Or is there any better alternative?
One way will be:
Make those variables public properties of your MainPage.
Assign the mainPage to be your child window DataContext.
Bind to those properties via XAML in your childWindow.
i am hoping that this will helps you....what you are trying to achieve....
ChildWindow Xaml file:
<controls:ChildWindow x:Class="ParentToChildWindow.ChildWindowControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
Width="400" Height="300"
Title="Pass Data from Parent to ChildWindow">
<Grid x:Name="LayoutRoot" Margin="2">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Vertical">
<TextBlock x:Name="txtValue" />
<TextBlock x:Name="txtName"/>
</StackPanel>
<Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" Width="75"
Height="23" HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="1" />
<Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75" Height="23"
HorizontalAlignment="Right" Margin="0,12,79,0" Grid.Row="1" />
</Grid>
</controls:ChildWindow>
ChildWindow CS file:
namespace ParentToChildWindow
{
using System.Windows;
using System.Windows.Controls;
public partial class ChildWindowControl : ChildWindow
{
public int Value { get; set; }
public string Name { get; set; }
public ChildWindowControl()
{
InitializeComponent();
}
private void OKButton_Click(object sender, RoutedEventArgs e)
{
this.txtValue.Text = this.Value.ToString();
this.txtName.Text = this.Name;
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
}
}
}
Parent CS File: I have added a button to parent XAML and added a click event
private void HandleButtonClickEvent(object sender, RoutedEventArgs e)
{
ChildWindowControl childControl = new ChildWindowControl();
childControl.Value = 10;
childControl.Name = "Data From Parent XAML to ChildWindow";
childControl.Show();
}
Pass the value in the constructor of theChildWindow
For this the place where we create the new instance of ChildWindow, we need to pass the required values to the Constructor. But remember, there must be a matching constructor already present in the ChildWindow control.
public ChildWindowControl(int value, string name)
{
InitializeComponent();
this.Value = value;
this.Name = name;
}
That is all that is required to pass the data from Parent XAML to ChildWindow

Categories