I was creating a custom control it is more like TabPage, where it's derive from View, containing a list of CSMenuItems and foreach menuItem is derived from BaseMenuItem and has menuContent that is derived from ContentView, like this :
• CSView
• CSMenuItem
• MenuContent
• Content
• CSMenuItem
• MenuContent
• Content
My problem is whenever I Change the properties value of MenuContent in the xaml file, the propertyChanged won't fire and MenuContent won't update. I am pretty sure the problem is in my renderers. Is there any way to update the child element in custom renderer?
Here are my codes for controls:
class CSView : View
{
public CSView ()
{
var items = new ObservableCollection <CSMenuItem>();
items.CollectionChanged += OnItemsChanged;
Items = items;
}
public static readonly BindableProperty ItemsProperty = BindableProperty.Create ("Items", typeof (IList <CSMenuItem>), typeof (CSView));
void OnItemsChanged (object sender, NotifyCollectionChangedEventArgs e)
{
foreach (CSMenuItem item in e.NewItems)
item.Parent = this;
// Maybe setting the item parent to this would be good?
// cause whenever new item is add I want its parent to be this.
// Correct me if im wrong.
}
public IList <CSMenuItem> Items
{
get => (IList <CSMenuItem>)GetValue (ItemsProperty);
set => SetValue (ItemsProperty, value);
}
class CSMenuItem : BaseMenuItem
{
public CSMenuItem()
{
}
public static readonly BindableProperty TitleProperty = BindableProperty.Create("Title", typeof(string), typeof(CSMenuItem), default);
public static readonly BindableProperty ContentProperty = BindableProperty.Create("Content", typeof(MenuContent), typeof(CSMenuItem));
public string Title
{
get => (string)GetValue (TitleProperty);
set => SetValue (TitleProperty, value);
}
public MenuContent Content
{
get => (MenuContent)GetValue (ContentProperty);
set => SetValue (ContentProperty, value);
}
}
class MenuContent : ContentView
{
public MenuContent ()
{
}
public static readonly BindableProperty TitleProperty = BindableProperty.Create ("Title", typeof (string), typeof (MenuContent));
public string Title
{
get => (string)GetValue (TitleProperty);
set => SetValue (TitleProperty, value);
}
}
and for renderers :
class CSViewRenderer : ViewRenderer<CSView, FrameLayout>, NavigationBarView.IOnItemSelectedListener
{
...
protected override void OnElementChanged (ElementChangedEventArgs <CSView> e)
{
if (e.OldElement != null)
UnhandleEvents (e.OldElement);
if (e.NewElement != null)
{
if (Control == null)
SetNativeControl (_control);
HandleEvents (e.NewElement)
}
}
void HandleEvents (CSView element)
=> element.PropertyChanged += OnElementPropertyChanged;
void UnhandleEvents (CSView element)
=> element.PropertyChanged -= OnElementPropertyChanged;
}
/// this is where the issue came from
class CSMenuItemRender : AbsItemRenderer
{
...
protected override View CreateNativeControl ()
{
_base = new LinearLayout (Context);
_toolBar = new ToolBar (Context)
_content = Platform.CreateRendererWithContext (Element.Content, Context); // where Element is type of MenuItem
_toolBar.Title = Element.Content.Title;
_base.Orientation = Orientation.Vertical;
_base.AddView (_toolBar, new LinearLayout.LayoutParams (-1, -2);
_base.AddView (_content.View, new LinearLayout.LayoutParams (-1, -2);
ElementRendererUtil.FitElement (Context, _base, _content);
Platform.SetRenderer (Element.Content, _content);
_content.ElementChanged += OnRendererElementChanged;
return _base;
}
void OnElementChanged (ElementChangedEventArgs <CSMenuItem> e)
{
if (e.OldElement != null)
UnhandleEvents (e.OldElement);
if (e.NewElement != null)
{
if (Control == null)
SetNativeControl (this)
HandleEvents (e.NewElement);
}
}
void OnRendererElementChanged (object sender, VisualElementChangedEventArgs e)
{
if (e.OldElement != null)
e.OldElement.PropertyChanged -= OnRendererElementPropertyChanged;
if (e.NewElement != null)
e.NewElement.PropertyChanged += OnRendererElementPropertyChanged;
}
void OnRendererElementPropertyChanged (object sender, PropertyChangedEventArgs e)
{
var menuContent = (MenuContent)sender;
if (e.PropertyName == MenuContent.TitleProperty.PropertyName)
_toolBar.Title = menuContent.Title;
else if (e.PropertyName == MenuContent.ContentProperty.PropertyName)
_content.Tracker.UpdateLayout ();
}
void HandleEvents (CSMenuItem element)
=> element.PropertyChanged += OnElementPropertyChanged;
void UnhandleEvents (CSMenuItem element)
=> element.PropertyChanged -= OnElementPropertyChanged;
}
I was able to change the MenuContent properties like Title when changing the derive type to Element, like this:
class MenuContent : Element
And setting its parent to CSMenuItem, like this :
public MenuContent Content
{
...
set {
if (value.Parent != this)
value.Parent = this;
SetValue (ContentProperty, value);
}
}
but still the Content property of MenuContent won't update when the value is changed. What I want is to update the MenuContent what ever the value changed in xaml file and to know why the propertyChanged won't fire.
Sorry for my bad English, hope you understand. May the Almighty Bless You Stay safe.