Is there a way to set this a font family? I'm unable to find how to do it. In what way I can implement this?
as i didn't find the direct property to set,here i use Custom Renderen to achive it (for Android):
create a AndroidShell.cs in Droid project:
[assembly: ExportRenderer(typeof(ShellTest.AppShell), typeof(AndroidShell))]
namespace ShellTest.Droid
{
class AndroidShell : ShellRenderer
{
public AndroidShell(Context context) : base(context)
{
}
protected override IShellBottomNavViewAppearanceTracker CreateBottomNavViewAppearanceTracker(ShellItem shellItem)
{
return new MyBottomNavigationView(this);
}
}
}
create MyBottomNavigationView.cs :
internal class MyBottomNavigationView: IShellBottomNavViewAppearanceTracker
{
private AndroidShell androidShell;
public MyBottomNavigationView(AndroidShell androidShell)
{
this.androidShell = androidShell;
}
public void Dispose()
{
}
public void ResetAppearance(BottomNavigationView bottomView)
{
IMenu menu = bottomView.Menu;
for (int i = 0; i < bottomView.Menu.Size(); i++)
{
IMenuItem menuItem = menu.GetItem(i);
var title = menuItem.TitleFormatted;
Typeface typeface = Typeface.CreateFromAsset(MainActivity.Instance.Assets, "HYXuJingXingKaiW.ttf");
SpannableStringBuilder sb = new SpannableStringBuilder(title);
sb.SetSpan(new CustomTypefaceSpan("",typeface), 0, sb.Length(), SpanTypes.InclusiveInclusive);
menuItem.SetTitle(sb);
}
}
public void SetAppearance(BottomNavigationView bottomView, ShellAppearance appearance)
{
}
}
create CustomTypefaceSpan.cs:
class CustomTypefaceSpan :TypefaceSpan
{
private Typeface newType;
public CustomTypefaceSpan(String family, Typeface type) : base(family)
{
newType = type;
}
public override void UpdateDrawState(TextPaint ds)
{
applyCustomTypeFace(ds,newType);
}
public override void UpdateMeasureState(TextPaint paint)
{
applyCustomTypeFace(paint,newType);
}
private static void applyCustomTypeFace(Paint paint, Typeface tf)
{
TypefaceStyle oldStyle;
Typeface old = paint.Typeface;
if (old == null)
{
oldStyle = 0;
}
else
{
oldStyle = old.Style;
}
TypefaceStyle fake = oldStyle & ~tf.Style;
if ((fake & TypefaceStyle.Bold) != 0)
{
paint.FakeBoldText = true;
}
if ((fake & TypefaceStyle.Italic) != 0)
{
paint.TextSkewX = -0.25f;
}
paint.SetTypeface(tf);
}
}
and HYXuJingXingKaiW.ttf is put inside Resources/Assets with BuildAction:AndroidAsset
in ios,you could try add this in AppDelegate.cs :
UITabBarItem.Appearance.SetTitleTextAttributes(new UITextAttributes() {Font=UIFont.FromName("HYXuJingXingKaiW.ttf",14), },UIControlState.Normal);
UITabBarItem.Appearance.SetTitleTextAttributes(new UITextAttributes() { Font = UIFont.FromName("HYXuJingXingKaiW.ttf", 14), }, UIControlState.Selected);
HYXuJingXingKaiW.ttf is put inside Resources
Leo Zhu's Answer is correct but didn't work for me until i put the ResetAppearance(bottomView) in SetAppearance Method. And i put this : if (title == null) { continue; } in ResetAppearance method.
At first, in your Android Folder, you need to add internal static MainActivity Instance { get; private set; } to MainActivity Class in MainActivity.cs and Initialize it by Instance = this; in OnCreate method in the same class.
public void SetAppearance(BottomNavigationView bottomView, IShellAppearanceElement appearance)
{
ResetAppearance(bottomView);
}
public void ResetAppearance(BottomNavigationView bottomView)
{
IMenu menu = bottomView.Menu;
for (int i = 0; i < bottomView.Menu.Size(); i++)
{
IMenuItem menuItem = menu.GetItem(i);
var title = menuItem.TitleFormatted;
if (title == null) { continue; }
Typeface typeface = Typeface.CreateFromAsset(MainActivity.Instance.Assets, "HYXuJingXingKaiW.ttf");
SpannableStringBuilder sb = new SpannableStringBuilder(title);
sb.SetSpan(new CustomTypefaceSpan("",typeface), 0, sb.Length(), SpanTypes.InclusiveInclusive);
menuItem.SetTitle(sb);
}
}
Related
I have a Tabbed page in my Xamarin Forms app. There I have two children content pages. There are two pickers and a button in the toolbar of the Tabbed page. What I want is to populate data from the server according to the values, which is changing in the pickers. I am using the MVVM design pattern. How can I pass picker values to the children pages? I tried to use the MessagingCenter, It works sometimes but not all the time.
Tabbedpage
public partial class LifekpiRankingTabbedRM : ZeroTabbarHeight
{
public LifekpiRankingTabbedRM(string uRegion)
{
InitializeComponent();
BindingContext = new LifekpiRankingRmTabbedViewModel(uRegion);
Children.Add(new LifeKpiRankingViewRM(uRegion));
Children.Add(new LifeKpiRankingViewRMsBM(uRegion));
}
}
Tabbedpage VM
public class LifekpiRankingRmTabbedViewModel : BaseViewModelHelper
{
public MonthData2 SelectedMonth
{
get => _selectedMonth;
set
{
_selectedMonth = value;
GenerateData(_selectedMonth.mIndex, SelectedKpi).Await(CompletedTask, ErrorHandle);
OnPropertyChanged();
}
}
public string SelectedKpi
{
get => _selectedKpi;
set
{
_selectedKpi = value;
GenerateData(SelectedMonth.mIndex, _selectedKpi).Await(CompletedTask, ErrorHandle);
OnPropertyChanged();
}
}
public LifekpiRankingRmTabbedViewModel(string uRegion)
{
this.uRegion = uRegion;
SelectedMonth = GetCurrentMonth();
SelectedKpi = KpiList.First();
reportType = "Monthly";
ReportTypeTapCommand = new Command(ChangeTypeCommand);
}
private async Task GenerateData(int selectedMonth, string selectedKpi)
{
AcrDialogClass.ShowLoadingDialog("Loading...");
if (runCount > 2)
{
var resRmData = await _apiServices.GetLifeRegionalKpiWiseRanking(selectedMonth, selectedKpi, isCumulative);
var resBmForRmData = await _apiServices.GetLifeBranchKpiWiseRankingForRM(selectedMonth, selectedKpi, uRegion, isCumulative);
if (resRmData != null)
{
MessagingCenter.Send<App, string>(App.Current as App, "KPI_REGINAL_RANKING", resRmData);
}
if (resBmForRmData != null)
{
MessagingCenter.Send<App, string>(App.Current as App, "KPI_BM_RANKING_FOR_RM", resBmForRmData);
}
}
else
{
runCount++;
}
}
private MonthData2 GetCurrentMonth()
{
var months = Enumerable.Range(1, 12).Select(i => new { I = i, M = DateTimeFormatInfo.InvariantInfo.GetMonthName(i) });
foreach (var ss in months)
{
MonthList.Add(new MonthData2() { mIndex = ss.I, mName = ss.M });
}
return MonthList.Find(r => r.mIndex == dt.Month);
}
private void ChangeTypeCommand()
{
if (isCumulative)
{
reportType = "Monthly";
isCumulative = false;
}
else
{
reportType = "Cumulative";
isCumulative = true;
}
GenerateData(SelectedMonth.mIndex, SelectedKpi).Await(CompletedTask, ErrorHandle);
}
private void CompletedTask()
{
AcrDialogClass.HideLoadingDialog();
}
private void ErrorHandle(Exception ex)
{
AcrDialogClass.HideLoadingDialog();
}
}
Tab 1
public partial class LifeKpiRankingViewRM : ContentPage
{
private LifeKpiRankingViewModelRM vm;
public LifeKpiRankingViewRM(string uRegion)
{
InitializeComponent();
BindingContext = new LifeKpiRankingViewModelRM(uRegion);
vm = BindingContext as LifeKpiRankingViewModelRM;
}
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Subscribe<App, string>(App.Current, "KPI_REGINAL_RANKING", (snd, arg) =>
{
vm.SetValues(arg);
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<App, string>(App.Current, "KPI_REGINAL_RANKING");
}
}
Tab 2
public partial class LifeKpiRankingViewRMsBM : ContentPage
{
private LifeKpiRankingViewModelBMForRM vm;
public LifeKpiRankingViewRMsBM(string uRegion)
{
InitializeComponent();
BindingContext = new LifeKpiRankingViewModelBMForRM(uRegion);
vm = BindingContext as LifeKpiRankingViewModelBMForRM;
}
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Subscribe<App, string>(App.Current, "KPI_BM_RANKING_FOR_RM", (snd, arg) =>
{
vm.SetValues(arg);
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<App, string>(App.Current, "KPI_BM_RANKING_FOR_RM");
}
}
At first, you can try to get the child page's instance and then set its value.
If you set the tabbedpage in the app.cs with MainPage = new LifekpiRankingTabbedRM():
var tabbedpage = App.Current.MainPage as LifekpiRankingTabbedRM;
var childpage1 = tabbedpage.Children[0];
var vm1 = childpage1.BindingContext as LifeKpiRankingViewModelRM;
vm1.SetValues(...);
Generally speaking, when you have the instance of the TabbedPage, you can get its child page and update the child page's value.
In addition, you can show more details about the MessageCenter not work and we can help you go on using the MessageCenter.
I have implemented WebViewRenderer it works well on Android but it doesn't work on iOS. It displays the value, but then user clicks next button and the value doesn't update. I have tried the the example on ms website, but that displayed no text, I don't have the values from URL. I have also tried several post here.
[assembly: ExportRenderer(typeof(ExtendedWebView), typeof(ExtendedWebViewRenderer))]
namespace MyApp.iOS.Renderers
{
public class ExtendedWebViewRenderer : ViewRenderer<ExtendedWebView, WKWebView>
{
WKWebView _wkWebView;
static ExtendedWebView _xwebView = null;
protected override void OnElementChanged(ElementChangedEventArgs<ExtendedWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
WKWebViewConfiguration config = new WKWebViewConfiguration();
_wkWebView = new WKWebView(Frame, config);
_wkWebView.NavigationDelegate = new CustomWKNavigationDelegate(this);
SetNativeControl(_wkWebView);
}
if (e.NewElement != null)
{
HtmlWebViewSource source = (Xamarin.Forms.HtmlWebViewSource)Element.Source;
string html = source.Html;
_wkWebView.LoadHtmlString(html, baseUrl: null);
_wkWebView.ScrollView.ScrollEnabled = false;
_wkWebView.SizeToFit();
}
}
}
public class CustomWKNavigationDelegate : WKNavigationDelegate
{
ExtendedWebViewRenderer _webViewRenderer;
public CustomWKNavigationDelegate(ExtendedWebViewRenderer webViewRenderer)
{
_webViewRenderer = webViewRenderer;
}
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
var wv = _webViewRenderer.Element as ExtendedWebView;
if (wv != null)
{
await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
wv.HeightRequest = (double)webView.ScrollView.ContentSize.Height;
}
}
}
}
[assembly: ExportRenderer(typeof(ExtendedWebView), typeof(ExtendedWebViewRenderer))]
namespace MyApp.Droid.Renderers
{
public class ExtendedWebViewRenderer : WebViewRenderer
{
public static int _webViewHeight;
static ExtendedWebView _xwebView = null;
public WebView _webView;
bool isScroll;
[Obsolete]
public ExtendedWebViewRenderer(Context context) : base(context)
{
}
class ExtendedWebViewClient : WebViewClient
{
WebView _webView;
public async override void OnPageFinished(WebView view, string url)
{
try
{
_webView = view;
if (_xwebView != null)
{
view.Settings.JavaScriptEnabled = true;
await Task.Delay(100);
string result = await _xwebView.EvaluateJavaScriptAsync("(function(){return document.body.scrollHeight;})()");
_xwebView.HeightRequest = Convert.ToDouble(result);
}
base.OnPageFinished(view, url);
}
catch (Exception ex)
{
Console.WriteLine($"{ex.Message}");
}
}
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request)
{
return true;
}
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
_xwebView = e.NewElement as ExtendedWebView;
_webView = Control;
if (e.OldElement == null)
{
_webView.SetWebViewClient(new ExtendedWebViewClient());
}
}
}
}
public class ExtendedWebView : WebView
{
public ExtendedWebView()
{
}
}
XAML
<controls:ExtendedWebView
VerticalOptions="FillAndExpand" Grid.RowSpan="2"
x:Name="enView"
Opacity="1"
Source="{Binding CZ, Mode=TwoWay}"/>
private static HtmlWebViewSource htmlSourceExplanation = new HtmlWebViewSource();
public HtmlWebViewSource Cz
{
get { return _cz; }
set
{
_cz = value;
NotifyPropertyChanged(nameof(Cz));
}
}
private void SetExplanationDetail(string text, string text2)
{
htmlSourceExplanation.Html = _style + "<div class=\"text\"><div>" + text + "</div><div>" + text2 + "</div></div>";
_cz = htmlSourceExplanation;
NotifyPropertyChanged("Cz");
}
I expect the value to change once to user cliks the button. `
I'm having trouble with the coding to properly listen for Firebase add or update events. My attempt below has the data loading into the Syncfusion Datagrid, but there is a weird glitch where when I click the mouse on the Datagrid and pull-down, the first record in my 4 record set gets added to the bottom of the Datagrid, showing a 5th record... if I update an element in the Datagrid, the change is not reflected in Firebase. If I add or change a value in firebase, it does not update in Datagrid. Any help to steer me in the right direction to get this to work would be appreciated. Here's the code:
the VisualStudio 2019
CookPage.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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Chart_sample"
xmlns:gauge="clr-namespace:Syncfusion.SfGauge.XForms;assembly=Syncfusion.SfGauge.XForms"
xmlns:Syncfusion="clr-namespace:Syncfusion.SfDataGrid.XForms;assembly=Syncfusion.SfDataGrid.XForms"
mc:Ignorable="d"
x:Class="Chart_sample.Views.CookPage">
<StackLayout>
<Syncfusion:SfDataGrid x:Name="sfGrid">
</Syncfusion:SfDataGrid>
</StackLayout>
</ContentPage>
CookPage.xaml.cs
using Chart_sample.Services;
using Syncfusion.SfDataGrid.XForms;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Chart_sample.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CookPage : ContentPage
{
FirebaseHelper firebaseHelper = new FirebaseHelper();
public CookPage()
{
InitializeComponent();
// for Syncfusion DataGrid
firebaseHelper.listenForEvents();
sfGrid.ItemsSource = ViewProgramModel._returnedEvents;
sfGrid.ColumnSizer = ColumnSizer.Star;
sfGrid.AllowEditing = true;
sfGrid.NavigationMode = NavigationMode.Cell;
sfGrid.SelectionMode = Syncfusion.SfDataGrid.XForms.SelectionMode.Single;
}
}
}
FirebaseHelper.cs
using Firebase.Database;
using Firebase.Database.Query;
using System;
using System.Linq;
namespace Chart_sample.Services
{
public class FirebaseHelper
{
internal ViewProgramModel ViewProgramModel { get; set; }
FirebaseClient firebase = new FirebaseClient("https://pelletpirate.firebaseio.com/");
private readonly string ChildProgram = "ControllerData/Pellet_Pirate_1/Program";
public static IDisposable returnedEvents;
public async void listenForEvents()
{
ViewProgramModel._returnedEvents.Clear();
var programs = await firebase.Child(ChildProgram).OnceAsync<ViewProgramModel>();
for (int i = 0; i < programs.Count; i++)
{
ViewProgramModel._returnedEvents.Add(programs.ElementAt(i).Object);
}
returnedEvents = firebase.Child(ChildProgram).OrderByKey().AsObservable<ViewProgramModel>()
.Subscribe(eventReceived =>
{
if (eventReceived.EventType == Firebase.Database.Streaming.FirebaseEventType.InsertOrUpdate)
{
var found = ViewProgramModel._returnedEvents.FirstOrDefault(i => i._KEY == eventReceived.Key);
if (found == null)
{
// not in observable collection, add it
ViewProgramModel._returnedEvents.Add(eventReceived.Object);
}
else
{
// event was updated
int tempIndex = ViewProgramModel._returnedEvents.IndexOf(found);
ViewProgramModel._returnedEvents[tempIndex] = eventReceived.Object;
}
}
});
}
}
}
ViewProgrammodel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace Chart_sample
{
public class ViewProgramModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _KEy;
private string MOde;
private int TArget;
private string TRigger;
private int TRiggerVAlue;
public string _KEY
{
get { return _KEy; }
set
{
this._KEy = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("_KEY"));
}
}
public string MODE
{
get { return MOde; }
set
{
this.MOde = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MODE"));
}
}
public int TARGET
{
get { return TArget; }
set
{
this.TArget = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TARGET"));
}
}
public string TRIGGER
{
get { return TRigger; }
set
{
this.TRigger = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TRIGGER"));
}
}
public int TRIGGERVALUE
{
get { return TRiggerVAlue; }
set
{
this.TRiggerVAlue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TRIGGERVALUE"));
}
}
public static ObservableCollection<ViewProgramModel> _returnedEvents = new ObservableCollection<ViewProgramModel>();
}
}
I edit your demo, I achieve the update, Add, delete function.
Here is running GIF.
I change your ViewProgramModel like following code. Just move the _returnedEvents to the FirebaseHelper.cs
namespace Chart_sample
{
public class ViewProgramModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _KEy;
private string MOde;
private int TArget;
private string TRigger;
private int TRiggerVAlue;
public string _KEY
{
get { return _KEy; }
set
{
this._KEy = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("_KEY"));
}
}
public string MODE
{
get { return MOde; }
set
{
this.MOde = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MODE"));
}
}
public int TARGET
{
get { return TArget; }
set
{
this.TArget = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TARGET"));
}
}
public string TRIGGER
{
get { return TRigger; }
set
{
this.TRigger = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TRIGGER"));
}
}
public int TRIGGERVALUE
{
get { return TRiggerVAlue; }
set
{
this.TRiggerVAlue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TRIGGERVALUE"));
}
}
}
Here is FirebaseHelper.cs, Note: I achieve the update function just for the TARGET Column, I suggest your to add a Primary-key(Auto-increase) for every record in your database to achieve your search function.
public class FirebaseHelper
{
public ObservableCollection<ViewProgramModel> _returnedEvents { get; set; }
public FirebaseHelper()
{
_returnedEvents = new ObservableCollection<ViewProgramModel>();
}
// internal ViewProgramModel MyViewProgramModel { get; set; }
FirebaseClient firebase = new FirebaseClient("https://xxxxxxxxxx.firebaseio.com/");
private readonly string ChildProgram = "ControllerData/xxxxxx_Pirate_1/Program";
public static IDisposable returnedEvents;
public async Task AddViewProgramModel()
{
//new ViewProgramModel() { MODE="test", TARGET=122, TRIGGER="122", TRIGGERVALUE=333, }
await firebase
.Child(ChildProgram)
.PostAsync( new ViewProgramModel() { MODE = "test", TARGET = 122, TRIGGER = "122", TRIGGERVALUE = 333, });
GetAllData();
}
public async Task UpdateViewProgramModel(ViewProgramModel viewProgramModel , string oldValue)
{
var toUpdatePerson = (await firebase
.Child(ChildProgram)
.OnceAsync<ViewProgramModel>()).FirstOrDefault(a => a.Object.TARGET == Convert.ToInt32( oldValue));
await firebase
.Child(ChildProgram)
.Child(toUpdatePerson.Key)
.PutAsync(viewProgramModel);
GetAllData();
}
public async Task DeleteViewProgramModel(string mode)
{
var toDeletePerson = (await firebase
.Child(ChildProgram)
.OnceAsync<ViewProgramModel>()).FirstOrDefault(a => a.Object.MODE == mode);
await firebase.Child(ChildProgram).Child(toDeletePerson.Key).DeleteAsync();
GetAllData();
}
public async void GetAllData()
{
_returnedEvents.Clear();
var programs = await firebase.Child(ChildProgram).OnceAsync<ViewProgramModel>();
for (int i = 0; i < programs.Count; i++)
{
_returnedEvents.Add(programs.ElementAt(i).Object);
}
}
public async void listenForEvents()
{
_returnedEvents.Clear();
var programs = await firebase.Child(ChildProgram).OnceAsync<ViewProgramModel>();
for (int i = 0; i < programs.Count; i++)
{
_returnedEvents.Add(programs.ElementAt(i).Object);
}
//returnedEvents = firebase.Child(ChildProgram).OrderByKey().AsObservable<ViewProgramModel>()
// .Subscribe(eventReceived =>
// {
// if (eventReceived.EventType == Firebase.Database.Streaming.FirebaseEventType.InsertOrUpdate)
// {
// var found = _returnedEvents.FirstOrDefault(i => i._KEY == eventReceived.Key);
// if (found == null)
// {
// // not in observable collection, add it
// _returnedEvents.Add(eventReceived.Object);
// }
// else
// {
// // event was updated
// int tempIndex = _returnedEvents.IndexOf(found);
// _returnedEvents[tempIndex] = eventReceived.Object;
// }
// }
//});
}
}
}
Here is CookPage.xaml
<StackLayout>
<Button Text="add" Clicked="Button_Clicked"></Button>
<Button Text="delete" Clicked="Button_Clicked_1"></Button>
<Syncfusion:SfDataGrid x:Name="sfGrid" ItemsSource="{Binding _returnedEvents, Mode=TwoWay} " >
</Syncfusion:SfDataGrid>
</StackLayout>
Here is code about CookPage.cs.
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CookPage : ContentPage
{
FirebaseHelper firebaseHelper = new FirebaseHelper();
public CookPage()
{
InitializeComponent();
// for Syncfusion DataGrid
firebaseHelper.listenForEvents();
//sfGrid.ItemsSource = ViewProgramModel._returnedEvents;
BindingContext= firebaseHelper;
sfGrid.ColumnSizer = ColumnSizer.Star;
sfGrid.AllowEditing = true;
sfGrid.NavigationMode = NavigationMode.Cell;
sfGrid.AllowLoadMore = true;
sfGrid.AutoGenerateColumns = true;
//sfGrid.AutoGenerateColumnsMode= AutoGenerateColumnsMode.
sfGrid.SelectionMode = Syncfusion.SfDataGrid.XForms.SelectionMode.Single;
sfGrid.AllowPullToRefresh = true;
sfGrid.CurrentCellEndEdit += SfGrid_CurrentCellEndEdit; ;
}
private async void SfGrid_CurrentCellEndEdit(object sender, GridCurrentCellEndEditEventArgs e)
{
//throw new System.NotImplementedException();
var selectObj = sender as SfDataGrid;
RowColumnIndex index = e.RowColumnIndex;
int selectColumnIndex = index.ColumnIndex; //2
int selectRowIndex = index.RowIndex; //3
var ob=firebaseHelper._returnedEvents;
ViewProgramModel selectObject =ob[selectRowIndex-1];
var newVale = e.NewValue.ToString();
var oldeValue = e.OldValue.ToString();
//Here just judge TARGET Column, you should judge all Columns
if (selectColumnIndex == 2)
{
selectObject.TARGET = Convert.ToInt32(newVale);
}
//If you want to achieve the all Grid change function, you should judge the selectRowIndex for every change
//if (selectRowIndex==1)
//{
// selectObject.MODE = newVale;
//}else if (selectRowIndex==2)
//{
// selectObject.TARGET = Convert.ToInt32( newVale);
//}else if (selectRowIndex == 3)
//{
// selectObject.TRIGGER = newVale;
//}else if (selectRowIndex == 4)
//{
// selectObject.TRIGGERVALUE = Convert.ToInt32(newVale);
//}
await firebaseHelper.UpdateViewProgramModel(selectObject, oldeValue);
}
private async void Button_Clicked(object sender, System.EventArgs e)
{
await firebaseHelper.AddViewProgramModel();
}
private async void Button_Clicked_1(object sender, System.EventArgs e)
{
await firebaseHelper.DeleteViewProgramModel("test");
}
}
I want to use tap and long tap together with Custom Gridview.
Tap is working but long tap not working.
it also works when one tap turns off the long tap.
Please help me.
Thank you.
public class GridView : Grid
{
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(GridView), default(IList), BindingMode.TwoWay);
public static readonly BindableProperty ItemTappedCommandProperty = BindableProperty.Create(nameof(ItemTappedCommand), typeof(ICommand), typeof(GridView), null);
public static readonly BindableProperty ItemLongTappedCommandProperty = BindableProperty.Create(nameof(ItemLongTappedCommand), typeof(ICommand), typeof(GridView), null);
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(GridView), default(DataTemplate));
public static readonly BindableProperty MaxColumnsProperty = BindableProperty.Create(nameof(MaxColumns), typeof(int), typeof(GridView), 2);
public static readonly BindableProperty TileHeightProperty = BindableProperty.Create(nameof(TileHeight), typeof(float), typeof(GridView), 220f);//adjusted here reuired height
public GridView()
{
PropertyChanged += GridView_PropertyChanged;
PropertyChanging += GridView_PropertyChanging;
}
public IList ItemsSource
{
get { return (IList)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public ICommand ItemTappedCommand
{
get { return (ICommand)GetValue(ItemTappedCommandProperty); }
set { SetValue(ItemTappedCommandProperty, value); }
}
public ICommand ItemLongTappedCommand
{
get { return (ICommand)GetValue(ItemLongTappedCommandProperty); }
set { SetValue(ItemLongTappedCommandProperty, value); }
}
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public int MaxColumns
{
get { return (int)GetValue(MaxColumnsProperty); }
set { SetValue(MaxColumnsProperty, value); }
}
public float TileHeight
{
get { return (float)GetValue(TileHeightProperty); }
set { SetValue(TileHeightProperty, value); }
}
private void BuildColumns()
{
ColumnDefinitions.Clear();
for (var i = 0; i < MaxColumns; i++)
{
ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
}
}
private View BuildTile(object item1)
{
var template = ItemTemplate.CreateContent() as View;
template.BindingContext = item1;
if (ItemTappedCommand != null)
{
var tapGestureRecognizer = new TapGestureRecognizer
{
Command = ItemTappedCommand,
CommandParameter = item1,
};
template.GestureRecognizers.Add(tapGestureRecognizer);
}
// Tap komutu eziyor.
if (ItemLongTappedCommand != null)
{
template.Effects.Add(new LongPressedEffect());
LongPressedEffect.SetCommand(template, ItemLongTappedCommand);
//LongPressedEffect.SetCommandParameter(template, item1);
}
return template;
}
private void BuildTiles()
{
// Wipe out the previous row & Column definitions if they're there.
if (RowDefinitions.Any())
{
RowDefinitions.Clear();
}
BuildColumns();
Children.Clear();
var tiles = ItemsSource;
if (tiles != null)
{
var numberOfRows = Math.Ceiling(tiles.Count / (float)MaxColumns);
for (var i = 0; i < numberOfRows; i++)
{
RowDefinitions.Add(new RowDefinition { Height = new GridLength(0, GridUnitType.Auto) });
}
for (var index = 0; index < tiles.Count; index++)
{
var column = index % MaxColumns;
var row = (int)Math.Floor(index / (float)MaxColumns);
var tile = BuildTile(tiles[index]);
Children.Add(tile, column, row);
}
}
}
private void GridView_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == ItemsSourceProperty.PropertyName)
{
var items = ItemsSource as INotifyCollectionChanged;
if (items != null)
items.CollectionChanged += ItemsCollectionChanged;
BuildTiles();
}
if (e.PropertyName == MaxColumnsProperty.PropertyName || e.PropertyName == TileHeightProperty.PropertyName)
{
BuildTiles();
}
}
private void GridView_PropertyChanging(object sender, Xamarin.Forms.PropertyChangingEventArgs e)
{
if (e.PropertyName == ItemsSourceProperty.PropertyName)
{
var items = ItemsSource as INotifyCollectionChanged;
if (items != null)
items.CollectionChanged -= ItemsCollectionChanged;
}
}
private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
BuildTiles();
}
}
public class LongPressedEffect : RoutingEffect
{
public LongPressedEffect() : base("MyApp.LongPressedEffect")
{ }
public static readonly BindableProperty CommandProperty = BindableProperty.CreateAttached("Command", typeof(ICommand), typeof(LongPressedEffect), (object)null);
public static ICommand GetCommand(BindableObject view)
{
//do something you want
Console.WriteLine("long press Gesture recognizer has been striked");
return (ICommand)view.GetValue(CommandProperty);
}
public static void SetCommand(BindableObject view, ICommand value)
{
view.SetValue(CommandProperty, value);
}
public static readonly BindableProperty CommandParameterProperty = BindableProperty.CreateAttached("CommandParameter", typeof(object), typeof(LongPressedEffect), (object)null);
public static object GetCommandParameter(BindableObject view)
{
return view.GetValue(CommandParameterProperty);
}
public static void SetCommandParameter(BindableObject view, object value)
{
view.SetValue(CommandParameterProperty, value);
}
}
I noticed you used Effect to create your own long pressed command. But if you consumed a TapGestureRecognizer at the same time, it will intercept the effect. Then your long pressed command won't be triggered.
You can either define the single tap click event in the effect to achieve them both. Here is my effect:
public class PressedEffect : RoutingEffect
{
public PressedEffect() : base("MyApp.PressedEffect")
{
}
public static readonly BindableProperty LongTapCommandProperty = BindableProperty.CreateAttached("LongTapCommand", typeof(ICommand), typeof(PressedEffect), (object)null);
public static ICommand GetLongTapCommand(BindableObject view)
{
return (ICommand)view.GetValue(LongTapCommandProperty);
}
public static void SetLongTapCommand(BindableObject view, ICommand value)
{
view.SetValue(LongTapCommandProperty, value);
}
public static readonly BindableProperty LongParameterProperty = BindableProperty.CreateAttached("LongParameter", typeof(object), typeof(PressedEffect), (object)null);
public static object GetLongParameter(BindableObject view)
{
return view.GetValue(LongParameterProperty);
}
public static void SetLongParameter(BindableObject view, object value)
{
view.SetValue(LongParameterProperty, value);
}
public static readonly BindableProperty TapCommandProperty = BindableProperty.CreateAttached("TapCommand", typeof(ICommand), typeof(PressedEffect), (object)null);
public static ICommand GetTapCommand(BindableObject view)
{
return (ICommand)view.GetValue(TapCommandProperty);
}
public static void SetTapCommand(BindableObject view, ICommand value)
{
view.SetValue(TapCommandProperty, value);
}
public static readonly BindableProperty TapParameterProperty = BindableProperty.CreateAttached("TapParameter", typeof(object), typeof(PressedEffect), (object)null);
public static object GetTapParameter(BindableObject view)
{
return view.GetValue(TapParameterProperty);
}
public static void SetTapParameter(BindableObject view, object value)
{
view.SetValue(TapParameterProperty, value);
}
}
Android implementation:
[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(AndroidPressedEffect), "PressedEffect")]
namespace PressedEffectDemo.Droid
{
public class AndroidPressedEffect : PlatformEffect
{
private bool _attached;
public static void Initialize() { }
public AndroidPressedEffect()
{
}
protected override void OnAttached()
{
if (!_attached)
{
if (Control != null)
{
Control.LongClickable = true;
Control.LongClick += Control_LongClick;
Control.Click += Control_Click;
}
else
{
Container.LongClickable = true;
Container.LongClick += Control_LongClick;
Container.Click += Control_Click;
}
_attached = true;
}
}
private void Control_Click(object sender, EventArgs e)
{
var command = PressedEffect.GetTapCommand(Element);
command?.Execute(PressedEffect.GetTapParameter(Element));
}
private void Control_LongClick(object sender, Android.Views.View.LongClickEventArgs e)
{
var command = PressedEffect.GetLongTapCommand(Element);
command?.Execute(PressedEffect.GetLongParameter(Element));
}
protected override void OnDetached()
{
if (_attached)
{
if (Control != null)
{
Control.LongClickable = true;
Control.LongClick -= Control_LongClick;
Control.Click -= Control_Click;
}
else
{
Container.LongClickable = true;
Container.LongClick -= Control_LongClick;
Control.Click -= Control_Click;
}
_attached = false;
}
}
}
}
iOS implementation:
[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(iOSPressedEffect), "PressedEffect")]
namespace PressedEffectDemo.iOS
{
public class iOSPressedEffect : PlatformEffect
{
private bool _attached;
private readonly UILongPressGestureRecognizer _longPressRecognizer;
private readonly UITapGestureRecognizer _tapRecognizer;
public iOSPressedEffect()
{
_longPressRecognizer = new UILongPressGestureRecognizer(HandleLongClick);
_tapRecognizer = new UITapGestureRecognizer(HandleClick);
}
protected override void OnAttached()
{
if (!_attached)
{
Container.AddGestureRecognizer(_longPressRecognizer);
Container.AddGestureRecognizer(_tapRecognizer);
_attached = true;
}
}
private void HandleClick()
{
var command = PressedEffect.GetTapCommand(Element);
command?.Execute(PressedEffect.GetTapParameter(Element));
}
private void HandleLongClick(UILongPressGestureRecognizer recognizer)
{
if (recognizer.State == UIGestureRecognizerState.Ended)
{
var command = PressedEffect.GetLongTapCommand(Element);
command?.Execute(PressedEffect.GetLongParameter(Element));
}
}
protected override void OnDetached()
{
if (_attached)
{
Container.RemoveGestureRecognizer(_longPressRecognizer);
Container.RemoveGestureRecognizer(_tapRecognizer);
_attached = false;
}
}
}
}
At last, you can consume them on XAML like:
<StackLayout>
<Grid HeightRequest="200"
BackgroundColor="Green"
local:PressedEffect.TapCommand="{Binding TapCommand}"
local:PressedEffect.LongTapCommand="{Binding LongTapCommand}">
<Grid.Effects>
<local:PressedEffect />
</Grid.Effects>
</Grid>
</StackLayout>
You could refer to my sample here.
I've got the following code:
Shared project:
using System;
using Xamarin.Forms;
namespace XamarinForms.Framework.Controls
{
public enum DrawerPosition
{
Left,
Right
}
public interface ISideDrawerNativeEventProxy
{
void RaiseSlideChanged(float percentage);
}
public class SideDrawer : Grid, ISideDrawerNativeEventProxy
{
#region DrawerSize
public static BindableProperty DrawerSizeProperty = BindableProperty.Create<SideDrawer, MaxedPercentSize>(d => d.DrawerSize, new MaxedPercentSize(MaxedPercentSizeType.Width, 80, 400));
public MaxedPercentSize DrawerSize
{
get { return (MaxedPercentSize) GetValue(DrawerSizeProperty); }
set { SetValue(DrawerSizeProperty, value); }
}
#endregion DrawerSize
#region IsOpen
public static BindableProperty IsOpenProperty = BindableProperty.Create<SideDrawer, bool>(d => d.IsOpen, default(bool));
public bool IsOpen
{
get { return (bool) GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
#endregion IsOpen
#region DrawerPosition
public static BindableProperty DrawerPositionProperty = BindableProperty.Create<SideDrawer, DrawerPosition>(d => d.DrawerPosition, default(DrawerPosition));
public DrawerPosition DrawerPosition
{
get { return (DrawerPosition) GetValue(DrawerPositionProperty); }
set { SetValue(DrawerPositionProperty, value); }
}
#endregion DrawerPosition
public static BindableProperty MainContentProperty = BindableProperty.Create<SideDrawer, View>(d => d.MainContent, default(View));
public View MainContent
{
get { return (View) GetValue(MainContentProperty); }
set { SetValue(MainContentProperty, value); }
}
public static BindableProperty DrawerContentProperty = BindableProperty.Create<SideDrawer, View>(d => d.DrawerContent, default(View));
public View DrawerContent
{
get { return (View) GetValue(DrawerContentProperty); }
set { SetValue(DrawerContentProperty, value); }
}
#region DrawerLength
public static BindableProperty DrawerLengthProperty = BindableProperty.Create<SideDrawer, int>(d => d.DrawerLength, default(int), defaultValueCreator: DrawerLengthDefault);
private static int DrawerLengthDefault(SideDrawer bindable)
{
return 300;
}
public int DrawerLength
{
get { return (int) GetValue(DrawerLengthProperty); }
set { SetValue(DrawerLengthProperty, value); }
}
#endregion DrawerLength
#region IsContentTranslated
public static BindableProperty IsContentTranslatedProperty = BindableProperty.Create<SideDrawer, bool>(d => d.IsContentTranslated, true);
public bool IsContentTranslated
{
get { return (bool) GetValue(IsContentTranslatedProperty); }
set { SetValue(IsContentTranslatedProperty, value); }
}
#endregion IsContentTranslated
void ISideDrawerNativeEventProxy.RaiseSlideChanged(float percentage)
{
}
}
}
Android:
using System;
using System.ComponentModel;
using Android.Support.V4.Widget;
using Android.Views;
using Android.Widget;
using Mobile.XamarinForms.Droid.Controls.SideDrawer;
using Mobile.XamarinForms.Framework.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Graphics;
using Application = Android.App.Application;
using Color = Android.Graphics.Color;
using RelativeLayout = Android.Widget.RelativeLayout;
using View = Android.Views.View;
[assembly: ExportRenderer(typeof(SideDrawer), typeof(SideDrawerRenderer))]
namespace Mobile.XamarinForms.Droid.Controls.SideDrawer
{
// public class SideDrawerRenderer : Xamarin.Forms.Platform.Android.AppCompat.ViewRenderer<Framework.Controls.SideDrawer, DrawerLayout>, DrawerLayout.IDrawerListener
public class SideDrawerRenderer : ViewRenderer<Framework.Controls.SideDrawer, DrawerLayout>, DrawerLayout.IDrawerListener
{
private DrawerLayout _nativeDrawerLayout;
private MarginLayoutParams _contentLayoutParameters;
private RelativeLayout _contentView;
private TextView _drawerView;
protected override void OnElementChanged(ElementChangedEventArgs<Framework.Controls.SideDrawer> e)
{
base.OnElementChanged(e);
if (this.Control == null)
{
InitializeNativeControl();
}
if (e.OldElement != null)
{
_nativeDrawerLayout.SetDrawerListener(null);
}
if (e.NewElement != null)
{
_nativeDrawerLayout.SetDrawerListener(this);
}
}
private void InitializeNativeControl()
{
_nativeDrawerLayout = new DrawerLayout(Context.ApplicationContext);
_nativeDrawerLayout.SetBackgroundColor(Element.BackgroundColor.ToAndroid());
_contentLayoutParameters = new MarginLayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent);
var layoutParamsDrawer = new DrawerLayout.LayoutParams(Element.DrawerLength, LinearLayout.LayoutParams.MatchParent);
layoutParamsDrawer.Gravity = GetDrawerGravity();
_drawerView = GetDrawerView(layoutParamsDrawer);
_contentView = GetContentView(_contentLayoutParameters);
// this one works, but i need the content from my forms property
var contentChild = new RelativeLayout(Context.ApplicationContext);
var contentChildLParams = new RelativeLayout.LayoutParams(300, 300);
contentChild.SetBackgroundColor(Color.Yellow);
_contentView.AddView(contentChild, contentChildLParams);
// i need to figure out how to make this work
// var contentRenderer = RendererFactory.GetRenderer(Element.MainContent);
// _contentView.AddView(contentRenderer.ViewGroup, new LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent));
_nativeDrawerLayout.AddView(_drawerView);
_nativeDrawerLayout.AddView(_contentView);
SetNativeControl(_nativeDrawerLayout);
}
private int GetDrawerGravity()
{
switch (Element.DrawerPosition)
{
case DrawerPosition.Left:
return (int)GravityFlags.Start;
case DrawerPosition.Right:
return (int)GravityFlags.End;
default:
throw new ArgumentOutOfRangeException();
}
}
private RelativeLayout GetContentView(LayoutParams layoutParameters)
{
var view = new RelativeLayout(Context.ApplicationContext);
view.LayoutParameters = layoutParameters;
view.SetBackgroundColor(Color.Red);
return view;
}
private TextView GetDrawerView(LayoutParams layoutParameters)
{
var view = new TextView(Context.ApplicationContext);
view.LayoutParameters = layoutParameters;
view.SetBackgroundColor(Color.Purple);
view.SetTextColor(Color.Blue);
view.SetText("just some text", TextView.BufferType.Editable);
return view;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
switch (e.PropertyName)
{
case nameof(Framework.Controls.SideDrawer.Height):
break;
case nameof(Framework.Controls.SideDrawer.Width):
break;
}
}
public void OnDrawerClosed(View drawerView)
{
Element.IsOpen = false;
System.Diagnostics.Debug.WriteLine("OnDrawerClosed");
}
public void OnDrawerOpened(View drawerView)
{
Element.IsOpen = true;
System.Diagnostics.Debug.WriteLine("OnDrawerOpened");
}
public void OnDrawerSlide(View drawerView, float slideOffset)
{
switch (Element.DrawerPosition)
{
case DrawerPosition.Left:
_contentView.TranslationX = (int) Math.Abs(Element.DrawerLength*slideOffset);
break;
case DrawerPosition.Right:
_contentView.TranslationX = (int) Math.Abs(Element.DrawerLength*slideOffset)*-1;
break;
default:
throw new ArgumentOutOfRangeException();
}
_nativeDrawerLayout.BringChildToFront(_drawerView);
_nativeDrawerLayout.RequestLayout();
((ISideDrawerNativeEventProxy) Element)?.RaiseSlideChanged(slideOffset);
}
public void OnDrawerStateChanged(int newState)
{
// not really needed
// System.Diagnostics.Debug.WriteLine($"OnDrawerStateChanged {newState}");
}
}
}
How do i output the contents of e.g. MainContent (Shared project property)?
I was unable to find anything in xamarin docs about this and support is rather quiet about this topic so far (guess they are too busy).
Does anyone have experience with this issue?
Update: Reproduction solution
Android container:
internal sealed class FormsElementWrapper : FormsViewGroup
{
public override bool OnInterceptTouchEvent(MotionEvent ev)
{
return false;
}
private readonly IVisualElementRenderer _view;
public FormsElementWrapper(Xamarin.Forms.View content) : base(Application.Context)
{
_view = content != null ? Platform.CreateRenderer(content) : null;
if (_view == null)
return;
AddView(_view.ViewGroup);
}
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
if (_view == null)
return;
_view.Element.Layout(new Rectangle(0.0, 0.0, ContextExtensions.FromPixels(Context, right - left), ContextExtensions.FromPixels(Context, bottom - top)));
_view.UpdateLayout();
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
MeasureSpecMode widthMode = MeasureSpec.GetMode(widthMeasureSpec);
MeasureSpecMode heightMode = MeasureSpec.GetMode(heightMeasureSpec);
int widthSize = MeasureSpec.GetSize(widthMeasureSpec);
int heightSize = MeasureSpec.GetSize(heightMeasureSpec);
int pxHeight = (int) ContextExtensions.ToPixels(Context, _view.Element.HeightRequest);
int pxWidth = (int) ContextExtensions.ToPixels(Context, _view.Element.WidthRequest);
var measuredWidth = widthMode != MeasureSpecMode.Exactly ? (widthMode != MeasureSpecMode.AtMost ? pxHeight : Math.Min(pxHeight, widthSize)) : widthSize;
var measuredHeight = heightMode != MeasureSpecMode.Exactly ? (heightMode != MeasureSpecMode.AtMost ? pxWidth : Math.Min(pxWidth, heightSize)) : heightSize;
SetMeasuredDimension(measuredWidth, measuredHeight);
}
}
android drawer:
using System;
using System.ComponentModel;
using Android.Support.V4.Widget;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Color = Android.Graphics.Color;
using RelativeLayout = Android.Widget.RelativeLayout;
using View = Android.Views.View;
[assembly: ExportRenderer(typeof(SideDrawer), typeof(SideDrawerRenderer))]
namespace MyNamespace.SideDrawer
{
public class SideDrawerRenderer : ViewRenderer<SideDrawer, DrawerLayout>, DrawerLayout.IDrawerListener
{
private DrawerLayout _nativeDrawerLayout;
private FormsElementWrapper _contentView;
private FormsElementWrapper _drawerView;
protected override void OnElementChanged(ElementChangedEventArgs<Framework.Controls.SideDrawer> e)
{
base.OnElementChanged(e);
if (this.Control == null)
{
InitializeNativeControl();
}
if (e.OldElement != null)
{
_nativeDrawerLayout.SetDrawerListener(null);
}
if (e.NewElement != null)
{
_nativeDrawerLayout.SetDrawerListener(this);
}
}
private void InitializeNativeControl()
{
_nativeDrawerLayout = new DrawerLayout(Context.ApplicationContext);
_nativeDrawerLayout.SetBackgroundColor(Element.BackgroundColor.ToAndroid());
AddDrawerLayer(_nativeDrawerLayout);
AddContentLayer(_nativeDrawerLayout);
SetNativeControl(_nativeDrawerLayout);
}
private void AddContentLayer(DrawerLayout nativeDrawerLayout)
{
_contentView = new FormsElementWrapper(Element.MainContent);
nativeDrawerLayout.AddView(_contentView);
}
private void AddDrawerLayer(DrawerLayout nativeDrawerLayout)
{
_drawerView = new FormsElementWrapper(Element.DrawerContent);
UpdateDrawerLength();
nativeDrawerLayout.AddView(_drawerView);
}
private int GetDrawerGravity()
{
switch (Element.DrawerPosition)
{
case DrawerPosition.Left:
return (int)GravityFlags.Start;
case DrawerPosition.Right:
return (int)GravityFlags.End;
default:
throw new ArgumentOutOfRangeException();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
switch (e.PropertyName)
{
case nameof(Framework.Controls.SideDrawer.Height):
break;
case nameof(Framework.Controls.SideDrawer.Width):
break;
case nameof(Framework.Controls.SideDrawer.MainContent):
_contentView = new FormsElementWrapper(Element.MainContent);
break;
case nameof(Framework.Controls.SideDrawer.DrawerContent):
_drawerView = new FormsElementWrapper(Element.DrawerContent);
break;
case nameof(Framework.Controls.SideDrawer.IsOpen):
UpdateDrawerStateProgramatically();
break;
case nameof(Framework.Controls.SideDrawer.DrawerLength):
case nameof(Framework.Controls.SideDrawer.DrawerPosition):
UpdateDrawerLength();
break;
}
}
private void UpdateDrawerLength()
{
var layoutParamsDrawer = new DrawerLayout.LayoutParams(Element.DrawerLength, ViewGroup.LayoutParams.MatchParent);
layoutParamsDrawer.Gravity = GetDrawerGravity();
_drawerView.LayoutParameters = layoutParamsDrawer;
}
private void UpdateDrawerStateProgramatically()
{
if (Element.IsOpen)
{
Control.OpenDrawer(GetDrawerGravity());
}
else
{
Control.CloseDrawer(GetDrawerGravity());
}
}
public void OnDrawerClosed(View drawerView)
{
Element.IsOpen = false;
System.Diagnostics.Debug.WriteLine("OnDrawerClosed");
}
public void OnDrawerOpened(View drawerView)
{
Element.IsOpen = true;
System.Diagnostics.Debug.WriteLine("OnDrawerOpened");
}
public void OnDrawerSlide(View drawerView, float slideOffset)
{
switch (Element.DrawerPosition)
{
case DrawerPosition.Left:
_contentView.TranslationX = (int)Math.Abs(Element.DrawerLength * slideOffset);
break;
case DrawerPosition.Right:
_contentView.TranslationX = (int)Math.Abs(Element.DrawerLength * slideOffset) * -1;
break;
default:
throw new ArgumentOutOfRangeException();
}
_nativeDrawerLayout.BringChildToFront(_drawerView);
_nativeDrawerLayout.RequestLayout();
((ISideDrawerNativeEventProxy)Element)?.RaiseSlideChanged(slideOffset);
}
public void OnDrawerStateChanged(int newState)
{
}
}
}
Xamarin.Forms Control:
public enum DrawerPosition
{
Left,
Right
}
public interface ISideDrawerNativeEventProxy
{
void RaiseSlideChanged(float percentage);
}
public class SideDrawer : Grid, ISideDrawerNativeEventProxy
{
#region IsOpen
public static BindableProperty IsOpenProperty = BindableProperty.Create<SideDrawer, bool>(d => d.IsOpen, default(bool), defaultBindingMode: BindingMode.TwoWay);
public bool IsOpen
{
get { return (bool) GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
#endregion IsOpen
#region DrawerPosition
public static BindableProperty DrawerPositionProperty = BindableProperty.Create<SideDrawer, DrawerPosition>(d => d.DrawerPosition, default(DrawerPosition));
public DrawerPosition DrawerPosition
{
get { return (DrawerPosition) GetValue(DrawerPositionProperty); }
set { SetValue(DrawerPositionProperty, value); }
}
#endregion DrawerPosition
public static BindableProperty MainContentProperty = BindableProperty.Create<SideDrawer, View>(d => d.MainContent, default(View), propertyChanging: MainContentPropertyChanging, propertyChanged: MainContentPropertyChanged);
private static void MainContentPropertyChanged(BindableObject bindable, View oldValue, View newValue)
{
if (newValue == null)
return;
newValue.Parent = (View)bindable;
}
private static void MainContentPropertyChanging(BindableObject bindable, View oldValue, View newValue)
{
if (oldValue == null)
return;
oldValue.Parent = null;
}
public View MainContent
{
get { return (View) GetValue(MainContentProperty); }
set { SetValue(MainContentProperty, value); }
}
public static BindableProperty DrawerContentProperty = BindableProperty.Create<SideDrawer, View>(d => d.DrawerContent, default(View), propertyChanging: DrawerContentPropertyChanging, propertyChanged: DrawerContentPropertyChanged);
private static void DrawerContentPropertyChanged(BindableObject bindable, View oldValue, View newValue)
{
if (newValue == null)
return;
newValue.Parent = (View)bindable;
}
private static void DrawerContentPropertyChanging(BindableObject bindable, View oldValue, View newValue)
{
if (oldValue == null)
return;
oldValue.Parent = null;
}
public View DrawerContent
{
get { return (View) GetValue(DrawerContentProperty); }
set { SetValue(DrawerContentProperty, value); }
}
#region DrawerLength
public static BindableProperty DrawerLengthProperty = BindableProperty.Create<SideDrawer, int>(d => d.DrawerLength, default(int), defaultValueCreator: DrawerLengthDefault);
private static int DrawerLengthDefault(SideDrawer bindable)
{
return 300;
}
public int DrawerLength
{
get { return (int) GetValue(DrawerLengthProperty); }
set { SetValue(DrawerLengthProperty, value); }
}
#endregion DrawerLength
#region IsContentTranslated
public static BindableProperty IsContentTranslatedProperty = BindableProperty.Create<SideDrawer, bool>(d => d.IsContentTranslated, true);
public bool IsContentTranslated
{
get { return (bool) GetValue(IsContentTranslatedProperty); }
set { SetValue(IsContentTranslatedProperty, value); }
}
#endregion IsContentTranslated
void ISideDrawerNativeEventProxy.RaiseSlideChanged(float percentage)
{
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (MainContent != null)
SetInheritedBindingContext(MainContent, BindingContext);
if (DrawerContent != null)
SetInheritedBindingContext(DrawerContent, BindingContext);
}
}
The major flaws in my original implementation were:
lack of measurement data being forwarded
not forwarding parent hierarchy
not inheriting bindingcontext