ASP.NET maintain control state that has collection items - asp.net

Interesting. I have a control QuickContacts with Contacts collection. When I declare Contacts of type List<Contact> it works perfectly, but when use ContactsList<Contact> which is a Generic that implements IList<T> it gives "Parser Error : Type 'Controls.ContactList'1[[Controls.Contact, Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' does not have a public property named 'Contact'."
Here, this code works perfectly with List<Contact>
[DefaultProperty("Contacts"),
ParseChildren(true, "Contacts"),
ToolboxData("<{0}:QuickContacts runat=server></{0}:QuickContacts>")]
public class QuickContacts : WebControl
{
private List<Contact> contactsList;
[Category("Behavior"),
Description("The contacts collection."),
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List<Contact> Contacts
{
get
{
if (contactsList == null)
{
contactsList = new List<Contact>();
}
return contactsList;
}
}
I want to provide a Contacts collection that maintains state so I replace using List<Contacts> with custom ContactsList that implements IList<T>, IStateManager. When use IList<T> instead of List<T>, it doesn't work. Here is the
public class ContactList<T> : IList<T>, IStateManager
{
Then I use it as following:
[DefaultProperty("Contacts"),
ParseChildren(true, "Contacts"),
ToolboxData("<{0}:QuickContacts runat=server></{0}:QuickContacts>")]
public class QuickContacts : WebControl
{
private ContactList<Contact> contactsList;
[Category("Behavior"),
Description("The contacts collection."),
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public ContactList<Contact> Contacts
{
get
{
if (contactsList == null)
{
contactsList = new ContactList<Contact>();
}
return contactsList;
}
}
"Parser Error : Type 'Controls.ContactList'1[[Controls.Contact, Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' does not have a public property named 'Contact'."
Parser Error at <cc1:Contact Name=
<asp:Content ID="Content2" ContentPlaceHolderID="Main" runat="server">
<cc1:QuickContacts ID="QuickContacts1" runat="server" BorderStyle="Solid" BorderWidth="1px">
<cc1:Contact Name="someone" Email="someone#example.com"
Phone="(555) 555-0100" />
I read many posts about List<T> vs IList<T> but that still doesn't answer the question. What difference between List<T> and a class that implements IList<T> which causes this error?

Here is the answer, when reviewed List<T> implementation at Microsoft http://referencesource.microsoft.com
[DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
[DebuggerDisplay("Count = {Count}")]
[Serializable]
public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>
{
It implements System.Collections.IList too. And this was the point.
And here is the full working code.
[DefaultProperty("Contacts"),
ParseChildren(true, ChildrenAsProperties = true, DefaultProperty = "Contacts"),
ToolboxData("<{0}:QuickContacts runat=server></{0}:QuickContacts>")]
public class QuickContacts : WebControl
{
private StateManagedCollection<Contact> contactsList;
[Category("Behavior"),
Description("The contacts collection."),
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public StateManagedCollection<Contact> Contacts
{
get
{
if (contactsList == null)
{
contactsList = new StateManagedCollection<Contact>();
}
return contactsList;
}
}
protected override void RenderContents(HtmlTextWriter writer)
{
Table t = CreateContactsTable();
if (t != null)
{
t.RenderControl(writer);
}
}
private Table CreateContactsTable()
{
Table t = null;
if (contactsList != null && contactsList.Count > 0)
{
t = new Table();
foreach (Contact item in contactsList)
{
Contact aContact = item as Contact;
if (aContact != null)
{
TableRow row = new TableRow();
TableCell c1 = new TableCell();
c1.Text = aContact.Name;
row.Controls.Add(c1);
TableCell c2 = new TableCell();
c2.Text = aContact.Email;
row.Controls.Add(c2);
TableCell c3 = new TableCell();
c3.Text = aContact.Phone;
row.Controls.Add(c3);
t.Controls.Add(row);
}
}
}
return t;
}
protected override void LoadViewState(object savedState)
{
if (savedState != null)
{
Pair p = savedState as Pair;
base.LoadViewState(p.First);
contactsList.LoadViewState(p.Second);
}
}
protected override object SaveViewState()
{
Pair p = new Pair(base.SaveViewState(), contactsList.SaveViewState());
return p;
}
protected override void TrackViewState()
{
base.TrackViewState();
contactsList.TrackViewState();
}
}
And here is the StateManagedCollection
public class StateManagedCollection<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>, IStateManager
where T : StateManagedClass, new()
{
private List<T> lst = default(List<T>);
private Boolean isTrackingViewState = default(Boolean);
private Boolean saveAll = default(Boolean);
public StateManagedCollection()
{
lst = new List<T>();
isTrackingViewState = false;
saveAll = false;
}
public void SetMarked()
{
for (int i = 0; i < lst.Count; i++)
{
lst[i].SetMarked();
}
}
public bool IsTrackingViewState
{
get { return isTrackingViewState; }
}
public void LoadViewState(object state)
{
if (state != null)
{
if (state is List<Object>)
{
// all items were saved
List<Object> allItems = (List<Object>)state;
saveAll = true;
Int32 count = lst.Count;
for (int i = 0; i < allItems.Count; i++)
{
if (i < count)
lst[i].LoadViewState(allItems[i]);
else
{
T item = new T();
item.LoadViewState(allItems[i]);
lst.Add(item);
}
}
}
else if (state is Dictionary<Int32, Object>)
{
Dictionary<Int32, Object> changedItems = (Dictionary<Int32, Object>)state;
foreach (KeyValuePair<Int32, Object> item in changedItems)
{
if (item.Key < lst.Count)
lst[item.Key].LoadViewState(item.Value);
}
}
}
}
public object SaveViewState()
{
if (saveAll)
{
List<Object> allItems = new List<Object>();
foreach (var item in lst)
{
item.SetMarked();
allItems.Add(item.SaveViewState());
}
return allItems;
}
else
{
Dictionary<Int32, Object> changedItems = new Dictionary<Int32, Object>();
for (int i = 0; i < lst.Count; i++)
{
Object state = lst[i].SaveViewState();
if (state != null)
{
changedItems.Add(i, state);
}
}
return changedItems;
}
}
public void TrackViewState()
{
isTrackingViewState = true;
for (int i = 0; i < lst.Count; i++)
{
lst[i].TrackViewState();
}
}
public int IndexOf(T item)
{
return lst.IndexOf(item);
}
public void Insert(int index, T item)
{
lst.Insert(index, item);
if (isTrackingViewState)
saveAll = true;
}
public void RemoveAt(int index)
{
lst.RemoveAt(index);
if (isTrackingViewState)
saveAll = true;
}
public T this[int index]
{
get
{
return lst[index];
}
set
{
lst[index] = value;
}
}
public void Add(T item)
{
lst.Add(item);
if (isTrackingViewState)
saveAll = true;
}
public void Clear()
{
lst.Clear();
if (isTrackingViewState)
saveAll = true;
}
public bool Contains(T item)
{
return lst.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
lst.CopyTo(array, arrayIndex);
}
public int Count
{
get { return lst.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
Boolean rslt = lst.Remove(item);
if (isTrackingViewState)
saveAll = true;
return rslt;
}
public IEnumerator<T> GetEnumerator()
{
return lst.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Add(object value)
{
Add((T)value);
return Count - 1;
}
public bool Contains(object value)
{
return Contains((T)value);
}
public int IndexOf(object value)
{
return IndexOf((T)value);
}
public void Insert(int index, object value)
{
Insert(index, (T)value);
}
public bool IsFixedSize
{
get { return ((IList)lst).IsFixedSize; }
}
public void Remove(object value)
{
Remove((T)value);
}
object IList.this[int index]
{
get
{
return lst[index];
}
set
{
lst[index] = (T)value;
}
}
public void CopyTo(Array array, int index)
{
CopyTo((T[])array, index);
}
public bool IsSynchronized
{
get { return ((ICollection)lst).IsSynchronized; }
}
public object SyncRoot
{
get { return ((ICollection)lst).SyncRoot; }
}
}
And the StateManagedClass
public abstract class StateManagedClass : IStateManager
{
private Boolean isTrackingViewState = default(Boolean);
private StateBag viewState = default(StateBag);
public StateManagedClass()
{
isTrackingViewState = false;
viewState = new StateBag(false);
}
public virtual StateBag ViewState
{
get
{
return viewState;
}
}
public bool IsTrackingViewState
{
get { return isTrackingViewState; }
}
public void LoadViewState(object state)
{
if (state != default(object))
{
((IStateManager)viewState).LoadViewState(state);
}
}
public object SaveViewState()
{
Object savedState = default(Object);
if (viewState != null)
{
savedState =
((IStateManager)viewState).SaveViewState();
}
return savedState;
}
public void TrackViewState()
{
isTrackingViewState = true;
if (viewState != default(StateBag))
{
((IStateManager)viewState).TrackViewState();
}
}
public void SetMarked()
{
viewState.SetDirty(true);
}
}
And the Contact class
public class Contact : StateManagedClass
{
[
Category("Behavior"),
DefaultValue(""),
Description("Name of contact"),
NotifyParentProperty(true)
]
public String Name
{
get
{
Object s = ViewState["Name"];
return (s == null) ? String.Empty : (String)s;
}
set
{
ViewState["Name"] = value;
}
}
[
Category("Behavior"),
DefaultValue(""),
Description("Email address of contact"),
NotifyParentProperty(true)
]
public String Email
{
get
{
Object s = ViewState["Email"];
return (s == null) ? String.Empty : (String)s;
}
set
{
ViewState["Email"] = value;
}
}
[
Category("Behavior"),
DefaultValue(""),
Description("Phone number of contact"),
NotifyParentProperty(true)
]
public String Phone
{
get
{
Object s = ViewState["Phone"];
return (s == null) ? String.Empty : (String)s;
}
set
{
ViewState["Phone"] = value;
}
}
}
Test the control:
<%# Page Title="" Language="C#" MasterPageFile="~/RTL.Master" AutoEventWireup="true" CodeBehind="WebForm15.aspx.cs" Inherits="Controls.WebForm15" %>
<%# Register Assembly="Controls" Namespace="Controls" TagPrefix="cc1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Main" runat="server">
<cc1:QuickContacts ID="QuickContacts1" runat="server" BorderStyle="Solid" BorderWidth="1px">
<cc1:Contact Name="someone" Email="someone#example.com"
Phone="(555) 555-0100" />
<cc1:Contact Name="jae" Email="jae#fourthcoffee.com"
Phone="(555) 555-0101" />
<cc1:Contact Name="lene" Email="lene#contoso.com"
Phone="(555) 555-0102" />
</cc1:QuickContacts>
<br />
<asp:Button runat="server" ID="Button1" Text="Add" OnClick="Button1_Click"></asp:Button>
<asp:Button runat="server" Text="Refresh"></asp:Button>
<br />
<br />
<asp:HyperLink ID="HyperLink1" NavigateUrl="~/WebForm15.aspx"
runat="server">
Reload Page</asp:HyperLink>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="script" runat="server">
</asp:Content>
And the codebehind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Controls
{
public partial class WebForm15 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
SkolaControlsWorkings.Controls.Contact contact = new SkolaControlsWorkings.Controls.Contact();
contact.Name = "Name";
contact.Email = "Email#mai.com";
contact.Phone = "(111) 111-1111";
QuickContacts1.Contacts.Add(contact);
Button1.Visible = false;
}
}
}
Now you can see how pressing the "Add" button causes new contact item to be added to the list, and how the new added contacts are maintained through page postbacks.
This is should be a complete working example, have fun.
Here is the MSDN original example Web Control Collection Property Example

Related

service doesn't start in sysoperation framework

Below are shown my classes of sysOperation framework, my problem is when i open the dialog and i press OK , nothing happens, what's wrong with this code?
My service class:
class ProdutionFLowsService extends SysOperationServiceBase
{
ProductionFlowId idOfCopy;
int copyToDo;
ProdTable prodTable;
public void process(ProdutionFLowsContract _contract)
{
this.getPromptParameters(_contract);
select firstonly ProdId
from prodTable
order by ProdId
where prodTable.ProductionFlowId == this.idOfCopy;
this.insertInProdTable();
}
public void insertInProdTable()
{
ProdTable _prodTable;
while(copyToDo > 0)
{
buf2Buf(prodTable,_prodTable);
_prodTable.RecId = 0;
_prodTable.ProdId = _prodTable.Type().initProdId(true);
_prodTable.GAP035ProductionFlowId = _prodTable.ProductionFlowId;
_prodTable.insert();
copyToDo--;
}
}
public void getPromptParameters(ProdutionFLowsContract _contract)
{
copyToDo = _contract.parmCopyToDo();
idOfCopy = _contract.parmidOfCopy();
}
}
My controller class:
class ProdutionFLowsController extends SysOperationServiceController
{
public void new()
{
super();
super(classStr(ProdutionFLowsService), methodStr(ProdutionFLowsService, process), SysOperationExecutionMode::Synchronous);
this.parmDialogCaption("TODO");
}
public static void main(Args _args)
{
ProdutionFLowsController controller = new ProdutionFLowsController();
controller.parmArgs(_args);
controller.startOperation();
}
}
My Contract Class:
[DataContractAttribute]
class ProdutionFLowsContract implements SysOperationInitializable,SysOperationValidatable
{
ProductionFlowId idOfCopy;
int copyToDo;
public void initialize()
{
idOfCopy = "";
copyToDo = 0;
}
[DataMemberAttribute("idOfCopy"),SysOperationLabelAttribute(literalStr("TODO(Id)")),SysOperationDisplayOrderAttribute("1")]
public ProductionFlowId parmidOfCopy(ProductionFlowId _idOfCopy = idOfCopy)
{
idOfCopy = _idOfCopy;
return idOfCopy;
}
[DataMemberAttribute("copyToDo"),SysOperationLabelAttribute(literalStr("copyToDo(Copy)")),SysOperationDisplayOrderAttribute("2")]
public int parmCopyToDo(int _copyToDo = copyToDo)
{
copyToDo = _copyToDo;
return copyToDo;
}
public boolean validate()
{
return false;
}
}
Your contract validation always fails because it always returns false and doesn't show any error in the infolog:
public boolean validate()
{
return false;
}
Try to replace return false with return true or to remove SysOperationValidatable and validate method altogether.

Xamarin Firebase and Syncfusion DataGrid. How do I listen for Firebase changes?

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");
}
}

Switch Toggling Event Using MVVM

I realized recently that a switch doesn't have a command. And i need to bind the toggling event to my view model. How do i go about it?
I tried to bind the command to Toggled event but the code is running into error
You can use EventToCommandBehavior to convert the event to command
create the EventToCommandBehavior class
using System;
using Xamarin.Forms;
namespace xxx
{
public class BehaviorBase<T> : Behavior<T> where T : BindableObject
{
public T AssociatedObject { get; private set; }
protected override void OnAttachedTo(T bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
if (bindable.BindingContext != null)
{
BindingContext = bindable.BindingContext;
}
bindable.BindingContextChanged += OnBindingContextChanged;
}
protected override void OnDetachingFrom(T bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
AssociatedObject = null;
}
void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
}
using System;
using System.Reflection;
using System.Windows.Input;
using Xamarin.Forms;
namespace xxx
{
public class EventToCommandBehavior : BehaviorBase<View>
{
Delegate eventHandler;
public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty InputConverterProperty = BindableProperty.Create("Converter", typeof(IValueConverter), typeof(EventToCommandBehavior), null);
public string EventName
{
get { return (string)GetValue(EventNameProperty); }
set { SetValue(EventNameProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public IValueConverter Converter
{
get { return (IValueConverter)GetValue(InputConverterProperty); }
set { SetValue(InputConverterProperty, value); }
}
protected override void OnAttachedTo(View bindable)
{
base.OnAttachedTo(bindable);
RegisterEvent(EventName);
}
protected override void OnDetachingFrom(View bindable)
{
DeregisterEvent(EventName);
base.OnDetachingFrom(bindable);
}
void RegisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));
}
MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
eventInfo.AddEventHandler(AssociatedObject, eventHandler);
}
void DeregisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
if (eventHandler == null)
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));
}
eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
eventHandler = null;
}
void OnEvent(object sender, object eventArgs)
{
if (Command == null)
{
return;
}
object resolvedParameter;
if (CommandParameter != null)
{
resolvedParameter = CommandParameter;
}
else if (Converter != null)
{
resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null);
}
else
{
resolvedParameter = eventArgs;
}
if (Command.CanExecute(resolvedParameter))
{
Command.Execute(resolvedParameter);
}
}
static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
{
var behavior = (EventToCommandBehavior)bindable;
if (behavior.AssociatedObject == null)
{
return;
}
string oldEventName = (string)oldValue;
string newEventName = (string)newValue;
behavior.DeregisterEvent(oldEventName);
behavior.RegisterEvent(newEventName);
}
}
}
in your xaml
<Switch >
<Switch.Behaviors>
<local:EventToCommandBehavior EventName="Toggled" Command="{Binding ToggledCommand}"/>
</Switch.Behaviors>
</Switch>
And in your ViewModel
public class MyViewModel
{
public ICommand ToggledCommand { get; private set; }
public MyViewModel()
{
ToggledCommand = new Command(() => {
// do some thing you want
});
}
}
I bind a bool property on my viewmodel to the IsToggled property on the switch, then handle when this changes in the viewmodel.

Xamarin Forms Custom GridView Tap and Long Tap Not Working Together

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.

How to render a xamarin.forms view in a custom renderer

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

Resources