I am using MVVMCross, and have a problem with MvxDialogFragment bindings.
I have a base service, which is resolved in Core PCL project, add custom services implementations in iOS and Android projects derived from the base service class.
In android service i construct and show MvxDialogFragment instance:
var top = (MvxFragmentActivity)Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity;
if (top == null)
{
throw new MvxException("Cannot get current top activity");
}
var dlg = new AlertDialog.Builder(top);
dlg.Create().Show();
dialog = new MyDialog
{
Cancelable = false
};
dialog.Show(top.SupportFragmentManager, "");
And i have simple dialog layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/test_click_button"
android:text="Test"
app:MvxBind="Click TestClickCommand" />
</LinearLayout>
So my goal is to acces base service commands from dialogFragment, which is instantiated from service. How can i do that?
As an alternative, i want to handle my button click in service, but cannot find a way to do this, because my View, ViewModel or Dialog properties are null.
How it's possible to handle clicks in service, or implement self binding?
Finally i achieved the desired, through MvxDialogFragment subscription, and service injection:
public class MyDialog : MvxDialogFragment
{
private ISampleService _sampleService;
public MyDialog(ISampleService sampleService)
{
_sampleService = sampleService;
}
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
EnsureBindingContextSet(savedInstanceState);
var dialog = new AlertDialog.Builder(Activity);
var view = this.BindingInflate(Resource.Layout.MyDialog, null);
view.FindViewById(Resource.Id.test_click_button).Click += (sender, e) =>
{
_sampleService.TestClick();
Dismiss();
};
dialog.SetView(view);
return dialog.Create();
}
}
Related
Can someone please tell me what I'm doing wrong? The RefreshView was just working yesterday and today I can't get it to work in any page. I created a brand new page with just a RefreshView and when I try to pull down it doesn't budge. Doesn't pull down, doesn't refresh, nothing. It was just working last night and today after no code changes it's not working. I've tried on the simulator and on my actual iPad. Before anyone suggests, there are no updates to any of my NuGet packages and I can't find any reference to this issue on Google.
XF: v5.0.0.2244
View:
<?xml version="1.0" encoding="utf-8" ?>
<views:MvxContentPage
x:Class="MyApp.UI.Pages.PricingPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:MyApp.Core.ViewModels;assembly=MyApp.Core"
xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
Title="{Binding Title}"
x:DataType="viewModels:PricingViewModel"
x:TypeArguments="viewModels:PricingViewModel">
<views:MvxContentPage.Content>
<RefreshView Command="{Binding RefreshCommand}" IsRefreshing="{Binding IsRefreshing}">
<CollectionView ItemsSource="{Binding MenuItems}" />
</RefreshView>
</views:MvxContentPage.Content>
</views:MvxContentPage>
ViewModel:
using System.Threading;
using System.Threading.Tasks;
using MyApp.Core.ChurromigosApi;
using MyApp.Core.Services;
using MyApp.Core.ViewModels.Base;
using MvvmCross.Commands;
using MvvmCross.ViewModels;
namespace MyApp.Core.ViewModels
{
public class PricingViewModel : BaseViewModel
{
private readonly IMenuItemService menuItemService;
public PricingViewModel(IMenuItemService menuItemService)
{
this.menuItemService = menuItemService;
this.RefreshCommand = new MvxAsyncCommand(this.Refresh);
this.MenuItems = new MvxObservableCollection<MenuItem>();
this.Title = "Pricing";
}
public MvxObservableCollection<MenuItem> MenuItems { get; set; }
public IMvxAsyncCommand RefreshCommand { get; }
public bool IsRefreshing { get; set; }
public override Task Initialize()
{
this.IsRefreshing = true;
return Task.CompletedTask;
}
private async Task Refresh()
{
var allMenuItems = await this.menuItemService.GetMenuItems(CancellationToken.None);
this.MenuItems.Clear();
this.MenuItems.AddRange(allMenuItems);
}
}
}
I found a similar problem with RefreshView, though in my case instead of becoming unresponsive, the app just crashes.
It appears that we need to pay more attention to the binding Mode for the property used for the IsRefreshing indicator:
If we want the RefreshView to automatically trigger the command when the VM's binded property is changed to True, then don't include a Mode, or use Mode=TwoWay, but don't manually change the value or you'll be stuck in a loop (or include a if(IsRefreshing) return; in your command or you wont get any results)
If we want to manually set the VM's property in the backend to show/hide the IsRefreshing activity indicator without triggering the command, then use Mode=OneWay or else you will be stuck in a loop.
In your particular case it could be related to the fact that you're setting your IsRefreshing property to true, and then you never set it back to false? I would wrap your Refreshing() method's code with a try, setting IsRefreshing=false at the beginning and then =false in a finally block.
In Xamarin Forms I tried to add show password feature to entry. so I toggled isPassword(true/false).
user has to click on the eye icon to see the password and it means that entry will lose focus.
Now, In iOs if user touches entry to enter more character or delete some. iOs will clear the entry and user has to enter again.
Is there a way to disable this?
We can Custom EntryRenderer to avoid that . Such as creating a CustomEntryRenderer in project.iOS solution :
[assembly: ExportRenderer(typeof(Entry), typeof(CustomEntryRenderer))]
namespace AppEntryTest.iOS
{
public class CustomEntryRenderer : EntryRenderer,IUITextFieldDelegate
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control.SecureTextEntry)
{
Control.Delegate = new MyTextFiledDelegate();
}
}
}
internal class MyTextFiledDelegate : UITextFieldDelegate
{
public override bool ShouldChangeCharacters(UITextField textField, NSRange range, string replacementString)
{
//return base.ShouldChangeCharacters(textField, range, replacementString);
string updatedString = textField.Text.Substring(0, (int)range.Location)+replacementString+textField.Text.Substring((int)(range.Location+range.Length));
textField.Text = updatedString;
return false;
}
}
}
Used in Xaml :
<StackLayout VerticalOptions="FillAndExpand" Padding="20">
<Entry Text="First Entry" IsPassword="True"/>
<Entry Placeholder="Second Entry"/>
</StackLayout>
The effect :
I have created ContenPage with WebView inside xaml (code below). I'am passing webpageURL in constructor. My target is to add item to webview localstorage using javascirpt, before even starting loading passed URL. I tried several methods like
Browser.EvaluateJavaScriptAsync(javascirpt);
or
Browser.Evaluate(javascirpt);
before executing in Constructor
Browser.Source = URL;
or wrapping this methods inside
Device.BeginInvokeOnMainThread(async () =>{});
but still they didn't work as supposed. Do you know any solutions to my problem ?
public partial class WebView : ContentPage
{
public WebView(string URL)
{
string item = "test";
string javascirpt = String.Format("localStorage.setItem('ls.item', '{0}')", item);
Browser.Eval(javascirpt);
Browser.Source = URL;
}
}
Xaml:
<WebView x:Name="Browser" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"
HeightRequest="1000"
WidthRequest="1000"
Navigating="WebOnNavigating"
Navigated="WebOnEndNavigating" />
We don't recommend that put these code in the constructor .Because the class "WebView" hasn't initialized yet when you call these code.Try to move them to method OnAppearing.
protected override void OnAppearing()
{
base.OnAppearing();
string item = "test";
string javascirpt = String.Format("localStorage.setItem('ls.item', '{0}')", item);
Browser.Eval(javascirpt);
Browser.Source = URL;
}
Using Visual Studio, when selecting 'Zebble for Xamarin - Cross Platform Solution' a default project will be created with five pages. I've modified the fifth page to implement a signature pad. Below is the following Page-5.zbl code.
<?xml version="1.0"?>
<z-Component z-type="Page5" z-base="Templates.Default" z-namespace="UI.Pages"
z-partial="true" Title="About us" data-TopMenu="MainMenu" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./../.zebble-schema.xml">
<z-place inside="Body">
<TextView Text="Hello world!" />
<SignaturePad Id="sigPad1" Enabled="true" LineThickness="4" Style.Border.Color="red" Style.Width="100" Style.Height="100"/>
</z-place>
</z-Component>
Which ends up adding this line to .zebble-generated.cs:
await Body.Add(sigPad1 = new SignaturePad { Id = "sigPad1", Enabled = true, LineThickness = 4 }
.Set(x => x.Style.Border.Color = "red")
.Set(x => x.Style.Width = 100)
.Set(x => x.Style.Height = 100));
I have been looking at this SignaturePad component package: https://github.com/xamarin/SignaturePad
If I wanted to use the Xamarian SignaturePad component or anyone else's SignaturePad component instead of the Zebble SignaturePad UI component, how would I do that?
To use a third party component, all you need to do is to create a Zebble wrapper around it. It's explained here:
http://zebble.net/docs/customrenderedview-third-party-native-components-plugins
Step 1: Creating Native Adapter(s)
You should first create a Zebble view class to represent an instance of your component using the following pattern. This class will be in the Shared project, available to all 3 platforms.
namespace Zebble.Plugin
{
partial class MyComponent : CustomRenderedView<MyComponentRenderer>
{
// TODO: Define properties, method, events, etc.
}
}
Note: To make the VS IntelliSense in ZBL files recognize this, you should create a .ZBL file for MyComponent as well:
<z-Component z-type="MyComponent" z-base="CustomRenderedView[MyComponentRenderer]" z-namespace="Zebble.Plugin"
z-partial="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./../.zebble-schema.xml" />
The next step will be to create the renderer classes.
Step 2: Creating Native Renderers(s)
You need to create the following class each platform (UWP, iOS, Android).
public class MyComponentRenderer : ICustomRenderer
{
MyComponent View;
TheNativeType Result;
public object Render(object view)
{
View = (MyComponent)view;
Result = new TheNativeType();
// TODO: configure the properties, events, etc.
return Result;
}
public void Dispose() => Result.Dispose();
}
Using it in the application code
In the application code (App.UI) you can use MyComponent just like any other built-in or custom view type.
<Zebble.Plugin.MyComponent Id="..." Property1="..." on-Event1="..." />
I am using stacklayout panel for the first time.
I read the gwt docs and found that parent for this should be a type of Layout.
Below is my code for parent widget("EncounterViewImpl") which creates a TabLayout panel and my widget "StudyViewImpl" is on one of the tabs of this tablayout.
EncounterViewImpl.ui.xml(Parent)
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder
xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:hpi="urn:import:com.zoomcare.emrgwt.client.ui.encounter.hpi"
xmlns:history="urn:import:com.zoomcare.emrgwt.client.ui.encounter.history"
xmlns:medication="urn:import:com.zoomcare.emrgwt.client.ui.encounter.medication"
xmlns:physical="urn:import:com.zoomcare.emrgwt.client.ui.encounter.physical"
xmlns:diagnosis="urn:import:com.zoomcare.emrgwt.client.ui.encounter.diagnosis"
xmlns:wellness="urn:import:com.zoomcare.emrgwt.client.ui.encounter.wellness"
xmlns:study="urn:import:com.zoomcare.emrgwt.client.ui.encounter.study"
xmlns:dental="urn:import:com.zoomcare.emrgwt.client.ui.encounter.dental">
<g:TabLayoutPanel barUnit="PX" barHeight="30" width="100%" height="100%">
<g:tab visible="false">
<g:header>
Dental
</g:header>
<g:ScrollPanel width="98%" height="100%">
<dental:EncounterDentalViewImpl ui:field="encounterDentalView"/>
</g:ScrollPanel>
</g:tab>
<g:tab visible="false">
<g:header>
Study
</g:header>
<g:ScrollPanel width="98%" height="100%">
<study:StudyViewImpl ui:field="studyView"/>
</g:ScrollPanel>
</g:tab>
</g:TabLayoutPanel>
StudyViewImpl.ui.xml(Child)
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<g:StackLayoutPanel height="100%" width="98%" ui:field="stackPanel" >
</g:StackLayoutPanel>
I want to add the widgets to stackpanel dynamically by its corresponding Activity class .StudyActivity calls setStudies(List list) after getting service response
Below is the code ..
StudyViewImpl.java
public class StudyViewImpl extends Composite implements StudyView {
private static LocalUiBinder uiBinder = GWT.create(LocalUiBinder.class);
interface LocalUiBinder extends UiBinder<StackLayoutPanel, StudyViewImpl> {
}
private StudyInfoActivityPiece presenter;
#UiField
StackLayoutPanel stackPanel ;
public StudyViewImpl() {
initWidget(uiBinder.createAndBindUi(this));
//stackPanel.setWidth("800");
}
public void setStudies(List<ScheduledStudyGWT> studies){
for(ScheduledStudyGWT study : studies) {
VerticalPanel vPanel = new VerticalPanel();
vPanel.setWidth("100%");
vPanel.setHeight("100%");
StudyInfoWidget infoWidget = new StudyInfoWidget();
infoWidget.setWidth("100%");
infoWidget.setHeight("100%");
infoWidget.populate(study);
infoWidget.setReadOnly(false);
ExamNoteWidget examNoteWidget = new ExamNoteWidget();
examNoteWidget.setWidth("100%");
examNoteWidget.setHeight("100%");
examNoteWidget.setExamNote(study.getExamNote());
examNoteWidget.setReadOnly(false);
InstructionsWidget instructionsWidget = new InstructionsWidget();
instructionsWidget.setInstructions(study.getInstructions());
instructionsWidget.setWidth("100%");
instructionsWidget.setHeight("100%");
instructionsWidget.setReadOnly(false);
vPanel.setVisible(true);
vPanel.add(infoWidget);
vPanel.add(examNoteWidget);
vPanel.add(instructionsWidget);
stackPanel.add(vPanel,createHeaderWidget(study.getOrderedLab().getLab().getName()),30);
}
}
private Widget createHeaderWidget(String text) {
HorizontalPanel hPanel = new HorizontalPanel();
hPanel.setHeight("100%");
hPanel.setSpacing(0);
hPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
HTML headerText = new HTML(text);
hPanel.add(headerText);
return new SimplePanel(hPanel);
}
}
When I run the above code I see that setStudies method is called by passing a proper list of objects but on the browser i see only headers.
Please help me figuring out the issue.
I got the issue fixed using StackPanel.I am running in quirk mode so shifted to StackPanel instead of StackLayoutPanel which is supported only in standard mode.