Xamarin.Forms / NakayokunaruHandsOn.xaml.cs
public App()
{
InitializeComponent();
var rootPage = new ContentPage();
var toRoundedBoxView = new Button
{
Text = "RoundedBoxView"
};
// toRoundedBoxView.Clicked += async (s, e) =>
// await rootPage.Navigation.PushAsync(new RoundedBoxViewPage());
var toEventBasedWebView = new Button
{
Text = "EventBasedWebView"
};
// toEventBasedWebView.Clicked += async (s, e) =>
// await rootPage.Navigation.PushAsync(new EventBasedWebViewPage());
var toMessageBasedWebView = new Button
{
Text = "MessageBasedWebView"
};
// toMessageBasedWebView.Clicked += async (s, e) =>
// await rootPage.Navigation.PushAsync(new MessageBasedWebViewPage());
rootPage.Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children =
{
toRoundedBoxView,
toEventBasedWebView,
toMessageBasedWebView,
},
};
MainPage = new NavigationPage(rootPage);
}
Xamarin.Forms / RoundedBoxView.cs
using System;
using Xamarin.Forms;
namespace NakayokunaruHandsOn
{
public class RoundedBoxView : View
{
#region CornerRadius BindableProperty
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create(nameof(CornerRadius), typeof(double), typeof(RoundedBoxView), 5.0);
public double CornerRadius
{
get { return (double)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
#endregion
#region Color BindableProperty
public static readonly BindableProperty ColorProperty =
BindableProperty.Create(nameof(Color), typeof(Color), typeof(RoundedBoxView), Color.Accent);
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
#endregion
// public event EventHandler Clicked;
// internal void SendClick()
// {
// Clicked?.Invoke(this, EventArgs.Empty);
// }
}
}
Android / RoundedBoxViewRenderer.cs
using System;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(NakayokunaruHandsOn.RoundedBoxView), typeof(NakayokunaruHandsOn.Droid.RoundedBoxViewRenderer))]
namespace NakayokunaruHandsOn.Droid
{
public class RoundedBoxViewRenderer : ViewRenderer<RoundedBoxView, Android.Views.View>
{
public RoundedBoxViewRenderer(Android.Content.Context context)
: base(context)
{
}
Android.Graphics.Drawables.GradientDrawable controlBackground;
protected override void OnElementChanged(ElementChangedEventArgs<RoundedBoxView> e)
{
if (Control == null)
{
var nativeControl = new Android.Views.View(Context);
controlBackground = new Android.Graphics.Drawables.GradientDrawable();
nativeControl.Background = controlBackground;
// nativeControl.Click += OnClick;
SetNativeControl(nativeControl);
}
if (e.NewElement != null)
{
UpdateRadius();
UpdateColor();
}
base.OnElementChanged(e);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == RoundedBoxView.CornerRadiusProperty.PropertyName)
{
UpdateRadius();
}
if (e.PropertyName == RoundedBoxView.ColorProperty.PropertyName)
{
UpdateColor();
}
}
private void UpdateRadius()
{
var radiusDp = (float)(Element.CornerRadius * Resources.DisplayMetrics.Density);
controlBackground.SetCornerRadius(radiusDp);
}
private void UpdateColor()
{
controlBackground.SetColor(Element.Color.ToAndroid());
}
// private void OnClick(object sender, EventArgs e)
// {
// Element?.SendClick();
// }
// protected override void Dispose(bool disposing)
// {
// if (Control != null)
// Control.Click -= OnClick;
// base.Dispose(disposing);
// }
}
}
iOS / RoundedBoxViewRenderer.cs
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using UIKit;
// Xamarin.FormsコントロールとRendererの対応を宣言
[assembly: ExportRenderer(typeof(NakayokunaruHandsOn.RoundedBoxView), typeof(NakayokunaruHandsOn.iOS.RoundedBoxViewRenderer))]
namespace NakayokunaruHandsOn.iOS
{
public class RoundedBoxViewRenderer : ViewRenderer<RoundedBoxView, UIView>
{
// private UITapGestureRecognizer tapGesuture;
protected override void OnElementChanged(ElementChangedEventArgs<RoundedBoxView> e)
{
if (Control == null)
{
var nativeControl = new UIView();
// tapGesuture = new UITapGestureRecognizer(() => Element?.SendClick());
// nativeControl.AddGestureRecognizer(tapGesuture);
SetNativeControl(nativeControl);
}
if (e.NewElement != null)
{
UpdateRadius();
UpdateColor();
}
base.OnElementChanged(e);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == RoundedBoxView.CornerRadiusProperty.PropertyName)
{
UpdateRadius();
}
if (e.PropertyName == RoundedBoxView.ColorProperty.PropertyName)
{
UpdateColor();
}
}
private void UpdateRadius()
{
Control.Layer.CornerRadius = (float)Element.CornerRadius;
}
private void UpdateColor()
{
Control.BackgroundColor = Element.Color.ToUIColor();
}
// protected override void Dispose(bool disposing)
// {
// if (Control != null)
// Control.RemoveGestureRecognizer(tapGesuture);
// base.Dispose(disposing);
// }
}
}
Xamarin.Forms / RoundedBoxViewPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NakayokunaruHandsOn"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true"
Title="RoundedBoxView"
x:Class="NakayokunaruHandsOn.RoundedBoxViewPage">
<StackLayout HorizontalOptions="Center"
VerticalOptions="Center">
<local:RoundedBoxView x:Name="roundedBox"
HeightRequest="100"
WidthRequest="100"
CornerRadius="10"/>
<Button Text="Next Color" Clicked="OnClicked" />
</StackLayout>
</ContentPage>
Xamarin.Forms / RoundedBoxViewPage.xaml.cs
using System;
using Xamarin.Forms;
namespace NakayokunaruHandsOn
{
public partial class RoundedBoxViewPage : ContentPage
{
public RoundedBoxViewPage()
{
InitializeComponent();
}
Random random = new Random ();
private void OnClicked (object sender, EventArgs e)
{
roundedBox.Color = Color.FromRgb (
random.Next (255),
random.Next (255),
random.Next (255));
}
}
}
Xamarin.Forms / AssemblyInfo.cs
[assembly: InternalsVisibleTo("NakayokunaruHandsOn.iOS")]
[assembly: InternalsVisibleTo("NakayokunaruHandsOn.Android")]
iOS / info.plist
<!-- ここを追加 -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Xamarin.Forms / EventBasedWebView.cs
using System;
using Xamarin.Forms;
namespace NakayokunaruHandsOn
{
public class EventBasedWebView : View
{
public void GoBack()
{
EventHandler handler = GoBackRequested;
if (handler != null)
{
handler.Invoke(this, EventArgs.Empty);
}
}
public void GoForward()
{
EventHandler handler = GoForwardRequested;
if (handler != null)
{
handler.Invoke(this, EventArgs.Empty);
}
}
public void Eval(string script)
{
EventHandler<EvalRequestedEventArgs> handler = EvalRequested;
if (handler != null)
{
handler.Invoke(this, new EvalRequestedEventArgs(script));
}
}
internal event EventHandler GoBackRequested;
internal event EventHandler GoForwardRequested;
internal event EventHandler<EvalRequestedEventArgs> EvalRequested;
}
public class EvalRequestedEventArgs
{
public string Script
{
get;
private set;
}
public EvalRequestedEventArgs(string script)
{
Script = script;
}
}
}
Android / EventBasedWebViewRenderer.cs
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using AWebView = Android.Webkit.WebView;
using Android.Webkit;
[assembly: ExportRenderer(typeof(NakayokunaruHandsOn.EventBasedWebView), typeof(NakayokunaruHandsOn.Droid.EventBasedWebViewRenderer))]
namespace NakayokunaruHandsOn.Droid
{
public class EventBasedWebViewRenderer : ViewRenderer<EventBasedWebView, AWebView>
{
public EventBasedWebViewRenderer(Android.Content.Context context)
: base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<EventBasedWebView> e)
{
if (Control == null)
{
var nativeControl = new AWebView(Context);
nativeControl.SetWebViewClient(new WebClient());
nativeControl.SetWebChromeClient(new WebChromeClient());
nativeControl.Settings.JavaScriptEnabled = true;
nativeControl.LoadUrl("https://ticktack.hatenablog.jp/entry/2016/06/11/124751");
SetNativeControl(nativeControl);
}
if (e.OldElement != null)
{
Element.GoBackRequested -= OnGoBackRequested;
Element.GoForwardRequested -= OnGoForwardRequested;
Element.EvalRequested -= OnEvalRequested;
}
if (e.NewElement != null)
{
Element.GoBackRequested += OnGoBackRequested;
Element.GoForwardRequested += OnGoForwardRequested;
Element.EvalRequested += OnEvalRequested;
}
base.OnElementChanged(e);
}
private void OnGoBackRequested(object sender, EventArgs e)
{
if (Control.CanGoBack())
{
Control.GoBack();
}
}
private void OnGoForwardRequested(object sender, EventArgs e)
{
if (Control.CanGoForward())
{
Control.GoForward();
}
}
private void OnEvalRequested(object sender, EvalRequestedEventArgs e)
{
Control.LoadUrl("javascript:" + e.Script);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Element.GoBackRequested -= OnGoBackRequested;
Element.GoForwardRequested -= OnGoForwardRequested;
Element.EvalRequested -= OnEvalRequested;
}
base.Dispose(disposing);
}
private class WebClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(AWebView view, string url)
{
view.LoadUrl(url);
return true;
}
}
}
}
iOS / EventBasedWebViewRenderer.cs
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using UIKit;
using Foundation;
[assembly: ExportRenderer(typeof(NakayokunaruHandsOn.EventBasedWebView), typeof(NakayokunaruHandsOn.iOS.EventBasedWebViewRenderer))]
namespace NakayokunaruHandsOn.iOS
{
public class EventBasedWebViewRenderer : ViewRenderer<EventBasedWebView, UIWebView>
{
protected override void OnElementChanged(ElementChangedEventArgs<EventBasedWebView> e)
{
if (Control == null)
{
var nativeControl = new UIWebView();
nativeControl.LoadRequest(new NSUrlRequest(new NSUrl("https://ticktack.hatenablog.jp/entry/2016/06/11/124751")));
SetNativeControl(nativeControl);
}
if (e.OldElement != null)
{
Element.GoBackRequested -= OnGoBackRequested;
Element.GoForwardRequested -= OnGoForwardRequested;
Element.EvalRequested -= OnEvalRequested;
}
if (e.NewElement != null)
{
Element.GoBackRequested += OnGoBackRequested;
Element.GoForwardRequested += OnGoForwardRequested;
Element.EvalRequested += OnEvalRequested;
}
base.OnElementChanged(e);
}
private void OnGoBackRequested(object sender, EventArgs e)
{
if (Control.CanGoBack)
{
Control.GoBack();
}
}
private void OnGoForwardRequested(object sender, EventArgs e)
{
if (Control.CanGoForward)
{
Control.GoForward();
}
}
private void OnEvalRequested(object sender, EvalRequestedEventArgs e)
{
Control.EvaluateJavascript(e.Script);
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
Control.Frame = this.Bounds;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Element.GoBackRequested -= OnGoBackRequested;
Element.GoForwardRequested -= OnGoForwardRequested;
Element.EvalRequested -= OnEvalRequested;
}
base.Dispose(disposing);
}
}
}
Xamarin.Forms / EventBasedWebViewPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NakayokunaruHandsOn"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true"
Title="EventBasedWebView"
x:Class="NakayokunaruHandsOn.EventBasedWebViewPage">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:EventBasedWebView Grid.Row="0" x:Name="webView" />
<StackLayout Grid.Row="1" Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Button Text="戻る" Clicked="GoBackClicked"/>
<Button Text="進む" Clicked="GoForwardClicked"/>
</StackLayout>
<StackLayout Grid.Row="2" Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Entry Text="location.reload()" x:Name="entry" HorizontalOptions="FillAndExpand"/>
<Button Text="Eval" Clicked="EvalClicked"/>
</StackLayout>
</Grid>
</ContentPage>
Xamarin.Forms / EventBasedWebViewPage.xaml.cs
using Xamarin.Forms;
namespace NakayokunaruHandsOn
{
public partial class EventBasedWebViewPage : ContentPage
{
public EventBasedWebViewPage()
{
InitializeComponent();
}
void GoBackClicked(object sender, System.EventArgs e)
{
webView.GoBack();
}
void GoForwardClicked(object sender, System.EventArgs e)
{
webView.GoForward();
}
void EvalClicked(object sender, System.EventArgs e)
{
webView.Eval(entry.Text);
}
}
}
Xamarin.Forms / MessageBasedWebView.cs
using Xamarin.Forms;
namespace NakayokunaruHandsOn
{
public class MessageBasedWebView : View
{
internal static readonly string GoBackKey = "MessageBasedWebView.GoBack";
internal static readonly string GoForwardKey = "MessageBasedWebView.GoForward";
internal static readonly string EvalKey = "MessageBasedWebView.Eval";
public void GoBack()
{
MessagingCenter.Send(this, GoBackKey);
}
public void GoForward()
{
MessagingCenter.Send(this, GoForwardKey);
}
public void Eval(string script)
{
MessagingCenter.Send(this, EvalKey, script);
}
}
}
Android / MessageBasedWebViewRenderer.cs
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using AWebView = Android.Webkit.WebView;
using Android.Webkit;
[assembly: ExportRenderer(typeof(NakayokunaruHandsOn.MessageBasedWebView), typeof(NakayokunaruHandsOn.Droid.MessageBasedWebViewRenderer))]
namespace NakayokunaruHandsOn.Droid
{
public class MessageBasedWebViewRenderer : ViewRenderer<MessageBasedWebView, AWebView>
{
public MessageBasedWebViewRenderer(Android.Content.Context context)
: base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<MessageBasedWebView> e)
{
if (Control == null)
{
var nativeControl = new AWebView(Context);
nativeControl.SetWebViewClient(new WebClient());
nativeControl.SetWebChromeClient(new WebChromeClient());
nativeControl.Settings.JavaScriptEnabled = true;
nativeControl.LoadUrl("http://ticktack.hatenablog.jp/entry/2016/06/11/124751");
SetNativeControl(nativeControl);
}
if (e.OldElement != null)
{
MessagingCenter.Unsubscribe<MessageBasedWebView>(this, MessageBasedWebView.GoBackKey);
MessagingCenter.Unsubscribe<MessageBasedWebView>(this, MessageBasedWebView.GoForwardKey);
MessagingCenter.Unsubscribe<MessageBasedWebView, string>(this, MessageBasedWebView.EvalKey);
}
if (e.NewElement != null)
{
MessagingCenter.Subscribe<MessageBasedWebView>(
this,
MessageBasedWebView.GoBackKey,
_ => OnGoBackRequested(),
e.NewElement);
MessagingCenter.Subscribe<MessageBasedWebView>(
this,
MessageBasedWebView.GoForwardKey,
_ => OnGoForwardRequested(),
e.NewElement);
MessagingCenter.Subscribe<MessageBasedWebView, string>(
this,
MessageBasedWebView.EvalKey,
(sender, args) => OnEvalRequested(args),
e.NewElement);
}
base.OnElementChanged(e);
}
private void OnGoBackRequested()
{
if (Control.CanGoBack())
{
Control.GoBack();
}
}
private void OnGoForwardRequested()
{
if (Control.CanGoForward())
{
Control.GoForward();
}
}
private void OnEvalRequested(string script)
{
Control.LoadUrl("javascript:" + script);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
MessagingCenter.Unsubscribe<MessageBasedWebView>(this, MessageBasedWebView.GoBackKey);
MessagingCenter.Unsubscribe<MessageBasedWebView>(this, MessageBasedWebView.GoForwardKey);
MessagingCenter.Unsubscribe<MessageBasedWebView, string>(this, MessageBasedWebView.EvalKey);
}
base.Dispose(disposing);
}
private class WebClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(AWebView view, string url)
{
view.LoadUrl(url);
return true;
}
}
}
}
iOS / MessageBasedWebViewRenderer.cs
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using UIKit;
using Foundation;
[assembly: ExportRenderer(typeof(NakayokunaruHandsOn.MessageBasedWebView), typeof(NakayokunaruHandsOn.iOS.MessageBasedWebViewRenderer))]
namespace NakayokunaruHandsOn.iOS
{
public class MessageBasedWebViewRenderer : ViewRenderer<MessageBasedWebView, UIWebView>
{
protected override void OnElementChanged(ElementChangedEventArgs<MessageBasedWebView> e)
{
if (Control == null)
{
var nativeControl = new UIWebView();
nativeControl.LoadRequest(new NSUrlRequest(new NSUrl("http://ticktack.hatenablog.jp/entry/2016/06/11/124751")));
SetNativeControl(nativeControl);
}
if (e.OldElement != null)
{
MessagingCenter.Unsubscribe<MessageBasedWebView>(this, MessageBasedWebView.GoBackKey);
MessagingCenter.Unsubscribe<MessageBasedWebView>(this, MessageBasedWebView.GoForwardKey);
MessagingCenter.Unsubscribe<MessageBasedWebView, string>(this, MessageBasedWebView.EvalKey);
}
if (e.NewElement != null)
{
MessagingCenter.Subscribe<MessageBasedWebView>(
this,
MessageBasedWebView.GoBackKey,
_ => OnGoBackRequested(),
e.NewElement);
MessagingCenter.Subscribe<MessageBasedWebView>(
this,
MessageBasedWebView.GoForwardKey,
_ => OnGoForwardRequested(),
e.NewElement);
MessagingCenter.Subscribe<MessageBasedWebView, string>(
this,
MessageBasedWebView.EvalKey,
(sender, args) => OnEvalRequested(args),
e.NewElement);
}
base.OnElementChanged(e);
}
private void OnGoBackRequested()
{
if (Control.CanGoBack)
{
Control.GoBack();
}
}
private void OnGoForwardRequested()
{
if (Control.CanGoForward)
{
Control.GoForward();
}
}
private void OnEvalRequested(string script)
{
Control.EvaluateJavascript(script);
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
Control.Frame = this.Bounds;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
MessagingCenter.Unsubscribe<MessageBasedWebView>(this, MessageBasedWebView.GoBackKey);
MessagingCenter.Unsubscribe<MessageBasedWebView>(this, MessageBasedWebView.GoForwardKey);
MessagingCenter.Unsubscribe<MessageBasedWebView, string>(this, MessageBasedWebView.EvalKey);
}
base.Dispose(disposing);
}
}
}
Xamarin.Forms / MessageBasedWebViewPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NakayokunaruHandsOn"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true"
Title="MessageBasedWebView"
x:Class="NakayokunaruHandsOn.MessageBasedWebViewPage">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:MessageBasedWebView Grid.Row="0" x:Name="webView" />
<StackLayout Grid.Row="1" Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Button Text="戻る" Clicked="GoBackClicked"/>
<Button Text="進む" Clicked="GoForwardClicked"/>
</StackLayout>
<StackLayout Grid.Row="2" Orientation="Horizontal">
<Entry Text="location.reload()" x:Name="entry" HorizontalOptions="FillAndExpand"/>
<Button Text="Eval" Clicked="EvalClicked"/>
</StackLayout>
</Grid>
</ContentPage>
Xamarin.Forms / MessageBasedWebViewPage.xaml.cs
using Xamarin.Forms;
namespace NakayokunaruHandsOn
{
public partial class MessageBasedWebViewPage : ContentPage
{
public MessageBasedWebViewPage()
{
InitializeComponent();
}
void GoBackClicked(object sender, System.EventArgs e)
{
webView.GoBack();
}
void GoForwardClicked(object sender, System.EventArgs e)
{
webView.GoForward();
}
void EvalClicked(object sender, System.EventArgs e)
{
webView.Eval(entry.Text);
}
}
}
Android / BorderEffect.cs
using System;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ResolutionGroupName("NakayokunaruHandsOn")]
[assembly: ExportEffect(typeof(NakayokunaruHandsOn.Droid.BorderEffect), "BorderEffect")]
namespace NakayokunaruHandsOn.Droid
{
public class BorderEffect : PlatformEffect
{
protected override void OnAttached()
{
if(Control == null)
return;
if(Control.Background == null)
{
var background = new Android.Graphics.Drawables.GradientDrawable();
Control.Background = background;
}
var drawable = Control.Background as Android.Graphics.Drawables.GradientDrawable;
if(drawable == null)
return;
drawable?.SetStroke(1, Color.Black.ToAndroid());
}
protected override void OnDetached()
{
var drawable = Control?.Background as Android.Graphics.Drawables.GradientDrawable;
if(drawable == null)
return;
drawable?.SetStroke(0, Color.Black.ToAndroid());
}
}
}
iOS / BorderEffect.cs
using System;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ResolutionGroupName("NakayokunaruHandsOn")]
[assembly: ExportEffect(typeof(NakayokunaruHandsOn.iOS.BorderEffect), "BorderEffect")]
namespace NakayokunaruHandsOn.iOS
{
public class BorderEffect : PlatformEffect
{
protected override void OnAttached()
{
if(Control == null)
return;
Control.Layer.BorderWidth = 1;
Control.Layer.BorderColor = Color.Black.ToCGColor();
}
protected override void OnDetached()
{
if(Control == null)
return;
Control.Layer.BorderWidth = 0;
}
}
}
Xamarin.Forms / EffectsSamplePage.xaml
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NakayokunaruHandsOn"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true"
Title="EffectsSample"
x:Class="NakayokunaruHandsOn.EffectsSamplePage">
<Label x:Name="label" Text="Effects Sample" VerticalOptions="Center" HorizontalOptions="Center"/>
</ContentPage>
Xamarin.Forms / EffectsSamplePage.xaml.cs
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace NakayokunaruHandsOn
{
public partial class EffectsSamplePage : ContentPage
{
public EffectsSamplePage()
{
InitializeComponent();
label.Effects.Add( Effect.Resolve("NakayokunaruHandsOn.BorderEffect") );
}
}
}
Xamarin.Forms / App.xaml.cs
public App()
{
...(省略)...
// ボタンを追加
var toEffectsSample = new Button
{
Text = "EffectsSample"
};
toEffectsSample.Clicked += async (s, e) =>
await rootPage.Navigation.PushAsync(new EffectsSamplePage());
rootPage.Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children =
{
toRoundedBoxView,
toEventBasedWebView,
toMessageBasedWebView,
toEffectsSample, // この行を追加
},
};
MainPage = new NavigationPage(rootPage);
}
Xamarin.Forms / BorderEffect.cs
using System;
using Xamarin.Forms;
namespace NakayokunaruHandsOn
{
public class BorderEffect : RoutingEffect
{
public BorderEffect() : base("NakayokunaruHandsOn.BorderEffect")
{
}
}
}
Xamarin.Forms / EffectsSamplePage.xaml.cs
//label.Effects.Add( Effect.Resolve("NakayokunaruHandsOn.BorderEffect") );
label.Effects.Add( new BorderEffect() ); // 代わりに追加するコード
Xamarin.Forms / ViewExtensions.cs
using System;
using System.Linq;
using Xamarin.Forms;
namespace NakayokunaruHandsOn
{
public static class ViewExtensions
{
public static readonly BindableProperty HasBorderProperty =
BindableProperty.CreateAttached("HasBorder", typeof(bool), typeof(ViewExtensions), false, propertyChanged: OnHasBorderChanged);
static void OnHasBorderChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = bindable as View;
if(view == null)
return;
var hasBorder = (bool)newValue;
if(hasBorder)
{
view.Effects.Add(new BorderEffect());
}
else
{
var toRemove = view.Effects.FirstOrDefault(x => x is BorderEffect);
if(toRemove != null)
{
view.Effects.Remove(toRemove);
}
}
}
public static readonly BindableProperty BorderColorProperty =
BindableProperty.CreateAttached("BorderColor", typeof(Color), typeof(ViewExtensions), Color.Accent);
public static void SetHasBorder(BindableObject view, bool hasBorder)
{
view.SetValue(HasBorderProperty, hasBorder);
}
public static bool GetHasBorder(BindableObject view)
{
return (bool)view.GetValue(HasBorderProperty);
}
public static void SetBorderColor(BindableObject view, Color color)
{
view.SetValue(BorderColorProperty, color);
}
public static Color GetBorderColor(BindableObject view)
{
return (Color)view.GetValue(BorderColorProperty);
}
class BorderEffect : RoutingEffect
{
public BorderEffect() : base("NakayokunaruHandsOn.BorderEffect")
{
}
}
}
}
Xamarin.Forms / EffectsSamplePage.xaml
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NakayokunaruHandsOn"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true"
Title="EffectsSample"
x:Class="NakayokunaruHandsOn.EffectsSamplePage">
<Label x:Name="label" Text="Effects Sample" VerticalOptions="Center" HorizontalOptions="Center"
local:ViewExtensions.HasBorder="true"
local:ViewExtensions.BorderColor="Red" />
</ContentPage>
Android / BorderEffect.cs
public class BorderEffect : PlatformEffect
{
protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
{
if(args.PropertyName == ViewExtensions.BorderColorProperty.PropertyName)
{
UpdateBorder();
}
}
protected override void OnAttached()
{
if(Control == null)
return;
if(Control.Background == null)
{
var background = new Android.Graphics.Drawables.GradientDrawable();
Control.Background = background;
}
UpdateBorder();
}
protected override void OnDetached()
{
var drawable = Control?.Background as Android.Graphics.Drawables.GradientDrawable;
if(drawable == null)
return;
drawable?.SetStroke(0, Color.Black.ToAndroid());
}
void UpdateBorder()
{
var drawable = Control?.Background as Android.Graphics.Drawables.GradientDrawable;
if(drawable == null)
return;
var color = ViewExtensions.GetBorderColor(Element);
drawable?.SetStroke(1, color.ToAndroid());
}
}