UPDATE
I am running into an issue with GetMapAsync Not calling OnMapReady I am able to suppress the error by checking if the map is null the map will then load with non custom markers. If I then move the map slightly OnMapReady will load and the custom markers will then be displayed. Below is my custom renderer class, if anyone else has ran into this issue I would be very grateful to hear your resolution.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Android.Content;
using Android.Gms.Maps;
using Android.Gms.Maps.Model;
using Android.Views;
using Android.Widget;
using BSi.Mobile.Events.Controls;
using BSi.Mobile.Events.Droid.Renderers;
using BSi.Mobile.Events.Events;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Maps.Android;
using View = Xamarin.Forms.View;
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace BSi.Mobile.Events.Droid.Renderers
{
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter, IOnMapReadyCallback
{
GoogleMap map;
IList<CustomPin> customPins;
private CustomPin selectedPin;
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
map.InfoWindowClick -= OnInfoWindowClick;
map.MarkerClick -= OnMarkerClick;
map.MapClick -= OnMapClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
selectedPin = formsMap.SelectedPin;
((MapView)Control).GetMapAsync(this);
}
}
public void OnMapReady(GoogleMap googleMap)
{
map = googleMap;
map.InfoWindowClick += OnInfoWindowClick;
map.MarkerClick += OnMarkerClick;
map.MapClick += OnMapClick;
map.SetInfoWindowAdapter(this);
SetMapMarkers();
}
private void SetMapMarkers()
{
map.Clear();
foreach (var pin in customPins)
{
addMarker(pin, false);
}
isDrawn = true;
if (selectedPin != null)
{
addMarker(selectedPin, true);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn && map != null)
{
SetMapMarkers();
}
else if (e.PropertyName.EndsWith("SelectedPin") && map != null)
{
var customMap = sender as CustomMap;
selectedPin = customMap?.SelectedPin;
if (selectedPin != null)
{
addMarker(selectedPin, true);
}
}
}
private void addMarker(CustomPin pin, bool isSelected)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude));
marker.SetTitle(pin.Pin.Label);
marker.SetSnippet(pin.Pin.Address);
switch (pin.Id)
{
case "Convention":
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.star));
break;
case "cafe":
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.cafe));
break;
case "bar":
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.bar));
break;
default:
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.restaurant));
break;
}
var selectedMarker = map.AddMarker(marker);
if (isSelected)
selectedMarker.ShowInfoWindow();
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
//Catches Exception incase user needs to update google play services
try
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
isDrawn = false;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (!string.IsNullOrWhiteSpace(customPin.Url))
{
var url = Android.Net.Uri.Parse(customPin.Url);
var intent = new Intent(Intent.ActionView, url);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
public Android.Views.View GetInfoContents(Marker marker)
{
var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as LayoutInflater;
if (inflater != null)
{
Android.Views.View view;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
switch (customPin.Id)
{
case "Conference":
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
break;
default:
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
break;
}
var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
var infoPhone = view.FindViewById<TextView>(Resource.Id.InfoWindowPhone);
var infoWebsite = view.FindViewById<TextView>(Resource.Id.InfoWindowWebsite);
if (infoTitle != null)
{
infoTitle.Text = marker.Title;
}
else
{
infoTitle.Visibility = ViewStates.Gone;
}
if (infoSubtitle != null)
{
infoSubtitle.Text = marker.Snippet;
}
else
{
infoSubtitle.Visibility = ViewStates.Gone;
}
if (infoPhone != null && customPin.Phone != null)
{
infoPhone.Text = customPin.Phone;
}
else
{
infoPhone.Visibility = ViewStates.Gone;
}
if (infoWebsite != null && customPin.Url != null)
{
infoWebsite.Text = customPin.Url;
}
else
{
infoWebsite.Visibility = ViewStates.Gone;
}
return view;
}
return null;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
return null;
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in customPins)
{
if (pin.Pin.Position == position)
{
return pin;
}
}
return null;
}
private void OnMarkerClick(object sender, GoogleMap.MarkerClickEventArgs e)
{
e.Handled = false;
App.EventAggregator?.GetEvent<MapClickedEvent>().Publish(new Pin { Position = new Position(e.Marker.Position.Latitude, e.Marker.Position.Longitude), Label = e.Marker.Title});
}
private void OnMapClick(object sender, GoogleMap.MapClickEventArgs e)
{
App.EventAggregator?.GetEvent<MapClickedEvent>().Publish(null);
}
}
}
I found a work around to fix this issue I moved the code where I setup the map markers inside its own function, from there I call that function inside OnElementPropertyChanged as with the normal execution. This sets up the markers when the map moves. To load the custom markers when the map loads I also called setup markers inside onMapReady. My Code Above now reflects a working solution, I hope I have helped anyone else out who may run into this issue Cheers!
Related
I have a sample pdf URL "http://www.africau.edu/images/default/sample.pdf". I want to show this pdf file and display in my mobile app using Xamarin.Forms. I tried using webviews and some custom renderers but did not work.
public class PdfViewRenderer : WebViewRenderer
{
internal class PdfWebChromeClient : WebChromeClient
{
public override bool OnJsAlert(Android.Webkit.WebView view, string url, string message, JsResult result)
{
if (message != "PdfViewer_app_scheme:print")
{
return base.OnJsAlert(view, url, message, result);
}
using (var printManager = Forms.Context.GetSystemService(Android.Content.Context.PrintService) as PrintManager)
{
printManager?.Print(FileName, new FilePrintDocumentAdapter(FileName, Uri), null);
}
return true;
}
public string Uri { private get; set; }
public string FileName { private get; set; }
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
return;
}
var pdfView = Element as PdfView;
if (pdfView == null)
{
return;
}
if (string.IsNullOrWhiteSpace(pdfView.Uri) == false)
{
Control.SetWebChromeClient(new PdfWebChromeClient
{
Uri = pdfView.Uri,
//FileName = GetFileNameFromUri(pdfView.Uri)
});
}
Control.Settings.AllowFileAccess = true;
Control.Settings.AllowUniversalAccessFromFileURLs = true;
LoadFile(pdfView.Uri);
}
private static string GetFileNameFromUri(string uri)
{
var lastIndexOf = uri?.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase);
return lastIndexOf > 0 ? uri.Substring(lastIndexOf.Value, uri.Length - lastIndexOf.Value) : string.Empty;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName != PdfView.UriProperty.PropertyName)
{
return;
}
var pdfView = Element as PdfView;
if (pdfView == null)
{
return;
}
if (string.IsNullOrWhiteSpace(pdfView.Uri) == false)
{
Control.SetWebChromeClient(new PdfWebChromeClient
{
Uri = pdfView.Uri,
// FileName = GetFileNameFromUri(pdfView.Uri)
});
}
LoadFile(pdfView.Uri);
}
private void LoadFile(string uri)
{
if (string.IsNullOrWhiteSpace(uri))
{
return;
}
//Control.Resources = new Uri(string.Format("ms-appx-web:///Assets/pdfjs/web/viewer.html?file={0}", string.Format("ms-appx-web:///Assets/Content/{0}", WebUtility.UrlEncode(PdfView.Uri))));
Control.LoadUrl($"file:///android_asset/pdfjs/web/viewer.html?file=file://{uri}");
//Control.LoadUrl(uri);
Control.LoadUrl(string.Format("ms-appx-web:///Assets/pdfjs/web/viewer.html?file={0}", string.Format("ms-appx-web:///Assets/Content/{0}", WebUtility.UrlEncode(uri))));
}
}
Your XAML
<WebView x:Name="Webview"
HeightRequest="1000"
WidthRequest="1000"
VerticalOptions="FillAndExpand"/>
Put your Webview source like this
Webview.Source = "https://docs.google.com/gview?
embedded=true&url="+"http://www.africau.edu/images/default/sample.pdf";
In Android
[assembly: ExportRenderer(typeof(CustomWebView), typeof(CustomWebViewRenderer))]
namespace DipsDemoXaml.Droid.Renderer
public class CustomWebViewRenderer : WebViewRenderer
{
public CustomWebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
Control.Settings.AllowUniversalAccessFromFileURLs = true;
Control.Settings.BuiltInZoomControls = true;
Control.Settings.DisplayZoomControls = true;
}
this.Control.SetBackgroundColor(Android.Graphics.Color.Transparent);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != "Uri") return;
var customWebView = Element as CustomWebView;
if (customWebView != null)
{
Control.LoadUrl(string.Format("file:///android_asset/pdfjs/web/viewer.html?file={0}", string.Format("file:///android_asset/Content/{0}", WebUtility.UrlEncode(customWebView.Uri))));
}
}
}
}
for iOS
[assembly: ExportRenderer(typeof(CustomWebView), typeof(CustomWebViewRenderer))]
namespace DipsDemoXaml.iOS.Renderes
{
public class CustomWebViewRenderer : ViewRenderer<CustomWebView, UIWebView>
{
protected override void OnElementChanged(ElementChangedEventArgs<CustomWebView>
e)
{
base.OnElementChanged(e);
if (Control == null)
{
SetNativeControl(new UIWebView());
}
if (e.OldElement != null)
{
// Cleanup
}
if (e.NewElement != null)
{
var customWebView = Element as CustomWebView;
string fileName = Path.Combine(NSBundle.MainBundle.BundlePath, string.Format("Content/{0}", WebUtility.UrlEncode(customWebView.Uri)));
Control.LoadRequest(new NSUrlRequest(new NSUrl(fileName, false)));
Control.ScalesPageToFit = true;
}
this.Opaque = false;
this.BackgroundColor = Color.Transparent.ToUIColor();
}
}
}
In shared
namespace DipsDemoXaml.Custom.Renderer
{
public class CustomWebView : WebView
{
public static readonly BindableProperty UriProperty =
BindableProperty.Create(nameof(Uri),typeof(string),
typeof(CustomWebView),default(string))
;
public string Uri
{
get => (string) GetValue(UriProperty);
set => SetValue(UriProperty, value);
}
}
}
In XAML You can Call Like this
<renderer:CustomWebView Uri="{Binding SelectedJournal.Uri}" />
I think Problem in your Custom Rendering You can Create a new property like URI in a string and You can Call the URI you need a way to access Android and iOS webview for that you can call android asset pdf viewer for android and for Ios you can pass a new NSUrl to load the pdf inside your app
I created a custom TimePicker and the renderers to android and iphone, with the objective to allow for that be nullable. As inspiration, was used the https://xamgirl.com/clearable-datepicker-in-xamarin-forms/
But, for some reason, the event is not firing when the time is set, thats happenen only in android, and more specific, back in android 8.1.
On shared project:
public class NullableTimePicker : TimePicker
{
public NullableTimePicker()
{
Time = DateTime.Now.TimeOfDay;
NullableTime = null;
Format = #"HH\:mm";
}
public string _originalFormat = null;
public static readonly BindableProperty PlaceHolderProperty =
BindableProperty.Create(nameof(PlaceHolder), typeof(string), typeof(NullableTimePicker), " : ");
public string PlaceHolder
{
get { return (string)GetValue(PlaceHolderProperty); }
set
{
SetValue(PlaceHolderProperty, value);
}
}
public static readonly BindableProperty NullableTimeProperty =
BindableProperty.Create(nameof(NullableTime), typeof(TimeSpan?), typeof(NullableTimePicker), null, defaultBindingMode: BindingMode.TwoWay);
public TimeSpan? NullableTime
{
get { return (TimeSpan?)GetValue(NullableTimeProperty); }
set { SetValue(NullableTimeProperty, value); UpdateTime(); }
}
private void UpdateTime()
{
if (NullableTime != null)
{
if (_originalFormat != null)
{
Format = _originalFormat;
}
}
else
{
Format = PlaceHolder;
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (BindingContext != null)
{
_originalFormat = Format;
UpdateTime();
}
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == TimeProperty.PropertyName ||
(
propertyName == IsFocusedProperty.PropertyName &&
!IsFocused &&
(Time == DateTime.Now.TimeOfDay)))
{
AssignValue();
}
if (propertyName == NullableTimeProperty.PropertyName && NullableTime.HasValue)
{
Time = NullableTime.Value;
if (Time == DateTime.Now.TimeOfDay)
{
//this code was done because when date selected is the actual date the"DateProperty" does not raise
UpdateTime();
}
}
}
public void CleanTime()
{
NullableTime = null;
UpdateTime();
}
public void AssignValue()
{
NullableTime = Time;
UpdateTime();
}
}
On Android project:
public class NullableTimePickerRenderer : ViewRenderer<NullableTimePicker, EditText>
{
public NullableTimePickerRenderer(Context context) : base(context)
{
}
TimePickerDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<NullableTimePicker> e)
{
base.OnElementChanged(e);
this.SetNativeControl(new Android.Widget.EditText(Context));
if (Control == null || e.NewElement == null)
return;
this.Control.Click += OnPickerClick;
if (Element.NullableTime.HasValue)
Control.Text = DateTime.Today.Add(Element.Time).ToString(Element.Format);
else
this.Control.Text = Element.PlaceHolder;
this.Control.KeyListener = null;
this.Control.FocusChange += OnPickerFocusChange;
this.Control.Enabled = Element.IsEnabled;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Xamarin.Forms.TimePicker.TimeProperty.PropertyName ||
e.PropertyName == Xamarin.Forms.TimePicker.FormatProperty.PropertyName)
SetTime(Element.Time);
}
void OnPickerFocusChange(object sender, Android.Views.View.FocusChangeEventArgs e)
{
if (e.HasFocus)
{
ShowTimePicker();
}
}
protected override void Dispose(bool disposing)
{
if (Control != null)
{
this.Control.Click -= OnPickerClick;
this.Control.FocusChange -= OnPickerFocusChange;
if (_dialog != null)
{
_dialog.Hide();
_dialog.Dispose();
_dialog = null;
}
}
base.Dispose(disposing);
}
void OnPickerClick(object sender, EventArgs e)
{
ShowTimePicker();
}
void SetTime(TimeSpan time)
{
Control.Text = DateTime.Today.Add(time).ToString(Element.Format);
Element.Time = time;
}
private void ShowTimePicker()
{
CreateTimePickerDialog(this.Element.Time.Hours, this.Element.Time.Minutes);
_dialog.Show();
}
void CreateTimePickerDialog(int hours, int minutes)
{
NullableTimePicker view = Element;
_dialog = new TimePickerDialog(Context, (o, e) =>
{
view.Time = new TimeSpan(hours: e.HourOfDay, minutes: e.Minute, seconds: 0);
view.AssignValue();
((IElementController)view).SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
Control.ClearFocus();
_dialog = null;
}, hours, minutes, true);
_dialog.SetButton("ok", (sender, e) =>
{
SetTime(Element.Time);
this.Element.Format = this.Element._originalFormat;
this.Element.AssignValue();
});
_dialog.SetButton2("clear", (sender, e) =>
{
this.Element.CleanTime();
Control.Text = this.Element.Format;
});
}
}
On iOS project:
public class NullableTimePickerRenderer : TimePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
{
base.OnElementChanged(e);
var timePicker = (UIDatePicker)Control.InputView;
timePicker.Locale = new NSLocale("no_nb");
if (e.NewElement != null && this.Control != null)
{
this.UpdateDoneButton();
this.AddClearButton();
this.Control.BorderStyle = UITextBorderStyle.Line;
Control.Layer.BorderColor = UIColor.LightGray.CGColor;
Control.Layer.BorderWidth = 1;
if (Device.Idiom == TargetIdiom.Tablet)
{
this.Control.Font = UIFont.SystemFontOfSize(25);
}
}
}
private void UpdateDoneButton()
{
var toolbar = (UIToolbar)Control.InputAccessoryView;
var doneBtn = toolbar.Items[1];
doneBtn.Clicked += (sender, args) =>
{
NullableTimePicker baseTimePicker = this.Element as NullableTimePicker;
if (!baseTimePicker.NullableTime.HasValue)
{
baseTimePicker.AssignValue();
}
};
}
private void AddClearButton()
{
var originalToolbar = this.Control.InputAccessoryView as UIToolbar;
if (originalToolbar != null && originalToolbar.Items.Length <= 2)
{
var clearButton = new UIBarButtonItem("clear", UIBarButtonItemStyle.Plain, ((sender, ev) =>
{
NullableTimePicker baseTimePicker = this.Element as NullableTimePicker;
this.Element.Unfocus();
this.Element.Time = DateTime.Now.TimeOfDay;
baseTimePicker.CleanTime();
}));
var newItems = new List<UIBarButtonItem>();
foreach (var item in originalToolbar.Items)
{
newItems.Add(item);
}
newItems.Insert(0, clearButton);
originalToolbar.Items = newItems.ToArray();
originalToolbar.SetNeedsDisplay();
}
}
}
This code works fine on iOS and Android version 8.1 or higher, but in lower version, this just not fire the event to set time, setting always the default time.
I'm also provided a git repo with the code, maybe, make easily understand my problem.
https://github.com/aismaniotto/Nullable24hTimePicker
Thanks for your help. After more digging on code and debugging, I realize the problem was on OkButton implementation. On Android 8, apparently, the callback from the TimePickerDialog was called before the OkButton implementation e what was there, is ignored. On the older version, the OKButton implementation was called before and, for some reason, cancel the invocation of the callback.
That way, removing the OkButton implementation, the problem was solved... remembering that on the working version, was ignored anyway. Eventually, I will commit to the repository, to be registered.
Thanks a lot.
I have an application using Xamarin Forms TabbedPage which has a feature that would allow the user to pause and play a page. Please see the code below.
Shared Code
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
var homePage = new NavigationPage(new HomePage())
{
Title = "Home",
Icon = "ionicons_2_0_1_home_outline_25.png"
};
var phrasesPage = new NavigationPage(new PhrasesPage())
{
Title = "Play",
Icon = "ionicons_2_0_1_play_outline_25.png"
};
Children.Add(homePage);
Children.Add(phrasesPage);
}
}
In iOS renderer:
public class TabbedPageRenderer : TabbedRenderer
{
private MainPage _page;
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
var tabbarController = (UITabBarController)this.ViewController;
if (null != tabbarController)
{
tabbarController.ViewControllerSelected += OnTabBarReselected;
}
}
void OnTabBarReselected(object sender, UITabBarSelectionEventArgs e)
{
var tabs = Element as TabbedPage;
var playTab = tabs.Children[4];
if (TabBar.SelectedItem.Title == "Play") {
if (tabs != null)
{
playTab.Title = "Pause";
playTab.Icon = "ionicons_2_0_1_pause_outline_22.png";
}
App.pauseCard = false;
}
else {
if (tabs != null) {
playTab.Title = "Play";
playTab.Icon = "ionicons_2_0_1_play_outline_25.png";
}
App.pauseCard = true;
}
}
Android Renderer
public class MyTabbedPageRenderer: TabbedPageRenderer, TabLayout.IOnTabSelectedListener
{
if (e.PropertyName == "Renderer")
{
viewPager = (ViewPager)ViewGroup.GetChildAt(0);
tabLayout = (TabLayout)ViewGroup.GetChildAt(1);
setup = true;
ColorStateList colors = null;
if ((int)Build.VERSION.SdkInt >= 23)
{
colors = Resources.GetColorStateList(Resource.Color.icon_tab, Forms.Context.Theme);
}
else
{
colors = Resources.GetColorStateList(Resource.Color.icon_tab);
}
for (int i = 0; i < tabLayout.TabCount; i++)
{
var tab = tabLayout.GetTabAt(i);
var icon = tab.Icon;
if (icon != null)
{
icon = Android.Support.V4.Graphics.Drawable.DrawableCompat.Wrap(icon);
Android.Support.V4.Graphics.Drawable.DrawableCompat.SetTintList(icon, colors);
}
}
}
void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
{
var tabs = Element as TabbedPage;
var playTab = tabs.Children[4];
var selectedPosition = tab.Position;
if(selectedPosition == 4)
{
if (playTab.Title == "Play")
{
if (tabs != null)
{
playTab.Title = "Pause";
playTab.Icon = "ionicons_2_0_1_pause_outline_22.png";
}
App.pauseCard = false;
}
else
{
if (tabs != null)
{
playTab.Title = "Play";
playTab.Icon = "ionicons_2_0_1_play_outline_25.png";
}
App.pauseCard = true;
}
}
}
}
This is perfectly working in iOS. But somehow in Android only the Title would change but not the Icon. Anyone knows what Im missing or how it should be done? Also, is this possible to be done in the shared code instead of repeating almost exactly the same lines on code in each platform?
You can do it by using the tab that is being passe to you in the OnTabReselected parameters in the TabRenderer.
You can move your whole logic with this object.
This is my whole renderer file (Android):
[assembly: ExportRenderer(typeof(SWTabSelection.MainPage), typeof(SWTabSelection.Droid.MyTabbedPageRenderer))]
namespace SWTabSelection.Droid
{
public class MyTabbedPageRenderer : TabbedPageRenderer, TabLayout.IOnTabSelectedListener
{
private ViewPager viewPager;
private TabLayout tabLayout;
private bool setup;
public MyTabbedPageRenderer() { }
public MyTabbedPageRenderer(Context context) : base(context)
{
//Use this constructor for newest versions of XF saving the context parameter
// in a field so it can be used later replacing the Xamarin.Forms.Forms.Context which is deprecated.
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == "Renderer")
{
viewPager = (ViewPager)ViewGroup.GetChildAt(0);
tabLayout = (TabLayout)ViewGroup.GetChildAt(1);
setup = true;
ColorStateList colors = GetTabColor();
for (int i = 0; i < tabLayout.TabCount; i++)
{
var tab = tabLayout.GetTabAt(i);
SetTintColor(tab, colors);
}
}
}
void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
{
// To have the logic only on he tab on position 1
if(tab == null || tab.Position != 1)
{
return;
}
if(tab.Text == "Play")
{
tab.SetText("Pause");
tab.SetIcon(Resource.Drawable.ionicons_2_0_1_pause_outline_25);
App.pauseCard = false;
}
else
{
tab.SetText("Play");
tab.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
SetTintColor(tab, GetTabColor());
}
void SetTintColor(TabLayout.Tab tab, ColorStateList colors)
{
var icon = tab?.Icon;
if(icon != null)
{
icon = Android.Support.V4.Graphics.Drawable.DrawableCompat.Wrap(icon);
Android.Support.V4.Graphics.Drawable.DrawableCompat.SetTintList(icon, colors);
}
}
ColorStateList GetTabColor()
{
return ((int)Build.VERSION.SdkInt >= 23)
? Resources.GetColorStateList(Resource.Color.icon_tab, Forms.Context.Theme)
: Resources.GetColorStateList(Resource.Color.icon_tab);
}
}
}
The only thing that I had with the code above is that the icon was not taking the Tint color so created a function with the same logic you had to set the Tint and I am using it on the Tab Reselection. If you have only one tab in your app you can set a global tint in the Android Theme/Style xml.
Hope this helps.
Custom Renderer is no needed , you can change the Title and Icon directly in Shared code.
Just implement CurrentPageChanged event in TabbedPage
Complete code
public partial class TabbedPage1 : TabbedPage
{
NavigationPage homePage;
NavigationPage phrasesPage;
public TabbedPage1 ()
{
InitializeComponent();
var homePage = new NavigationPage(new Page1())
{
Title = "Home",
Icon = "1.png"
};
var phrasesPage = new NavigationPage (new Page2())
{
Title = "Play",
Icon = "1.png"
};
Children.Add(homePage);
Children.Add(phrasesPage);
this.CurrentPageChanged += (object sender, EventArgs e) => {
var i = this.Children.IndexOf(this.CurrentPage);
if (i == 0)
{
homePage.Title = "HomeChanged";
homePage.Icon = "2.png";
}
else {
phrasesPage.Title = "PlayChanged";
phrasesPage.Icon = "2.png";
}
};
}
}
Result
PS: Make the image files access from a different platform.
iOS - Resources
Android - Resources->drawable
There isn't a way to detect when a tab is reselected in Xamarin.Forms, so we'll have to use custom rederers to detect the logic.
For Android, we'll have to handle two cases: Current Tab Page Changed, and Current Tab Page Reselected.
We'll subscribe to to CurrentPageChanged and in its EventHandler, we'll check to see if the tab selected is PhrasesPage. If it is, we'll update the Icon/Text.
In OnTabReselected, we can check which page is currently selected, and if it's the PhrasesPage, we can update PhrasesPage.Icon and PhrasesPage.Text.
Sample App
https://github.com/brminnick/ChangeTabbedPageIconSample/tree/master
Android Custom Renderer
[assembly: ExportRenderer(typeof(MainPage), typeof(TabbedPageRenderer))]
namespace YourNameSpace
{
public class TabbedPageRenderer : TabbedRenderer, TabLayout.IOnTabSelectedListener
{
//Overloaded Constructor required for Xamarin.Forms v2.5+
public TabbedPageRenderer(Android.Content.Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
Element.CurrentPageChanged += HandleCurrentPageChanged;
}
void HandleCurrentPageChanged(object sender, EventArgs e)
{
var currentNavigationPage = Element.CurrentPage as NavigationPage;
if (!(currentNavigationPage.RootPage is PhrasesPage))
return;
var tabLayout = (TabLayout)ViewGroup.GetChildAt(1);
for (int i = 0; i < tabLayout.TabCount; i++)
{
var currentTab = tabLayout.GetTabAt(i);
var currentTabText = currentTab.Text;
if (currentTabText.Equals("Play") || currentTabText.Equals("Pause"))
{
Device.BeginInvokeOnMainThread(() => UpdateTab(currentTabText, currentTab, currentNavigationPage));
break;
}
}
}
void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
{
System.Diagnostics.Debug.WriteLine("Tab Reselected");
var mainPage = Application.Current.MainPage as MainPage;
var currentNavigationPage = mainPage.CurrentPage as NavigationPage;
if(currentNavigationPage.RootPage is PhrasesPage)
Device.BeginInvokeOnMainThread(() => UpdateTab(currentNavigationPage.Title, tab, currentNavigationPage));
}
void UpdateTab(string currentTabText, TabLayout.Tab tab, NavigationPage currentNavigationPage)
{
if (currentTabText.Equals("Puzzle"))
{
tab.SetIcon(IdFromTitle("Settings", ResourceManager.DrawableClass));
currentNavigationPage.Title = "Settings";
}
else
{
tab.SetIcon(IdFromTitle("Puzzle", ResourceManager.DrawableClass));
currentNavigationPage.Title = "Puzzle";
}
}
int IdFromTitle(string title, Type type)
{
string name = System.IO.Path.GetFileNameWithoutExtension(title);
int id = GetId(type, name);
return id;
}
int GetId(Type type, string memberName)
{
object value = type.GetFields().FirstOrDefault(p => p.Name == memberName)?.GetValue(type)
?? type.GetProperties().FirstOrDefault(p => p.Name == memberName)?.GetValue(type);
if (value is int)
return (int)value;
return 0;
}
}
}
I think you are using the custom render for tabbed page customization. For, Android you should refer the icon from Resource.Drawable. Please try with below code snippet in android renderer.
public class CustomTabRenderer: TabbedRenderer
{
private Activity _act;
protected override void OnModelChanged(VisualElement oldModel, VisualElement newModel)
{
base.OnModelChanged(oldModel, newModel);
_act = this.Context as Activity;
}
// You can do the below function anywhere.
public override void OnWindowFocusChanged(bool hasWindowFocus)
{
ActionBar actionBar = _act.ActionBar;
if (actionBar.TabCount > 0)
{
Android.App.ActionBar.Tab tabOne = actionBar.GetTabAt(0);
tabOne.SetIcon(Resource.Drawable.shell);
}
base.OnWindowFocusChanged(hasWindowFocus);
}
}
Refer this also: https://forums.xamarin.com/discussion/17654/tabbedpage-icons-not-visible-android
Try adding this code to OnElementChanged in TabbedPageRenderer
if (e.PropertyName == "Renderer")
{
ViewPager pager = (ViewPager)ViewGroup.GetChildAt(0);
TabLayout layout = (TabLayout)ViewGroup.GetChildAt(1);
for (int i = 0; i < layout.TabCount; i++)
{
var tab = layout.GetTabAt(i);
var icon = tab.Icon;
if (icon != null)
{
icon = Android.Support.V4.Graphics.Drawable.DrawableCompat.Wrap(icon);
Android.Support.V4.Graphics.Drawable.DrawableCompat.SetTintList(icon, colors);
}
}
}
More info here : https://montemagno.com/xamarin-forms-android-selected-and-unselected-tab-colors/
I get a problem when I am trying to show an AlertDialog in my adapter. It's showed many time when I want to delete an article (The first article for each category) or when I try to delete a category (Exactly when I remove a second category )
This is my code
ArticlesConfigurations.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using SymaMobile.Core.Models;
using Vici.CoolStorage;
using SymaMobile.Droid.Adapters;
using Android.Database;
namespace SymaMobile.Droid
{
public class ArticlesConfiguration : Fragment
{
private ListView listViewCatgArticles;
private ListCategorieArticlesConfigAdapter adapterCatConfig;
private ListArticleConfigAdapter adapterArticles;
private List<CategoriesArticlesConfig> listCatgArticles;
private List<ArticlesConfig> listArticles;
private Button ajouterArticle;
private GridView gridArticles;
private ArticlesConfig art;
private TextView codeBarre;
private TextView libelle;
private CategoriesArticlesConfig categorieActuelle;
private Articles articleActuelle;
private CSList<CategoriesArticles, int> catglist;
private Spinner categorie;
private Spinner articles;
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.Inflate(Resource.Layout.fragment_configuration_articles, container, false);
listViewCatgArticles = v.FindViewById<ListView>(Resource.Id.lv_articles_fragment_configuration_articles);
ajouterArticle = v.FindViewById<Button>(Resource.Id.bt_ajouter_fragment_configuration_articles);
gridArticles = v.FindViewById<GridView>(Resource.Id.gv_articles_fragment_articles_configuration);
listCatgArticles = CategoriesArticlesConfig.List().ToList<CategoriesArticlesConfig>();
adapterCatConfig = new ListCategorieArticlesConfigAdapter(Activity, listCatgArticles);
listViewCatgArticles.Adapter = adapterCatConfig;
ajouterArticle.Click += ajouterArticle_Click;
listViewCatgArticles.ItemClick += listViewCatgArticles_ItemClick;
gridArticles.ItemClick += gridArticles_ItemClick;
return v;
}
void gridArticles_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
if (e.Position == gridArticles.Count-1)
{
Android.Util.Log.Error("Position grid", ""+e.Position);
Android.Util.Log.Error("grid Count", "" + gridArticles.Count);
AlertDialog.Builder builder = new AlertDialog.Builder(Activity);
builder.SetTitle(Resource.String.ajouter_categorie);
LayoutInflater inflater = Activity.LayoutInflater;
View v = inflater.Inflate(Resource.Layout.alertdialog_ajouter_article_configuration, null);
codeBarre = v.FindViewById<TextView>(Resource.Id.ed_code_barre_alert_dialog_ajouter_article_configuration);
libelle = v.FindViewById<TextView>(Resource.Id.tv_nom_article_alert_dialog_ajouter_article_configuration);
categorie = v.FindViewById<Spinner>(Resource.Id.sp_categories_alert_dialog_ajouter_article_configuration);
articles = v.FindViewById<Spinner>(Resource.Id.sp_articles_alert_dialog_ajouter_article_configuration);
var adapter = new ArrayAdapter<string>(Activity, Android.Resource.Layout.SimpleSpinnerDropDownItem);
catglist = CategoriesArticles.List();
for (int i = 0; i < catglist.Count; i++)
{
adapter.Add(catglist[i].Nom);
}
categorie.ItemSelected += categorie_ItemSelected;
categorie.Adapter = adapter;
codeBarre.TextChanged+=codeBarre_TextChanged;
builder.SetPositiveButton(Resource.String.ajouter, delegate
{
if (articleActuelle != null && categorieActuelle != null)
{
ArticlesConfig a = ArticlesConfig.New();
a.Article = articleActuelle;
a.CategorieArticles = categorieActuelle;
a.Save();
listArticles.Add(a);
adapterArticles.NotifyDataSetChanged();
}
});
builder.SetNegativeButton(Resource.String.annuler, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.SetView(v);
builder.Show();
}
}
void categorie_ItemSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
List<Articles> a = catglist[e.Position].Articles.ToList<Articles>();
var adapter = new ArrayAdapter<string>(Activity, Android.Resource.Layout.SimpleSpinnerDropDownItem);
for (int i = 0; i < a.Count; i++)
{
adapter.Add(a[i].Libelle);
}
articles.Adapter = adapter;
}
private void codeBarre_TextChanged(object sender, Android.Text.TextChangedEventArgs e)
{
if (codeBarre.Text.Length > 2)
{
articleActuelle = Articles.ReadFirst("CodeBarre ='" + codeBarre.Text + "'");
if (articleActuelle != null)
{
libelle.Text = articleActuelle.Libelle;
}
else
{
libelle.Text = "";
}
}
}
void listViewCatgArticles_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
categorieActuelle = CategoriesArticlesConfig.Read((int)adapterCatConfig.GetItemId(e.Position));
listArticles = categorieActuelle.ArticlesConfig.ToList();
adapterArticles = new ListArticleConfigAdapter(Activity, listArticles);
gridArticles.Adapter = adapterArticles;
}
void ajouterArticle_Click(object sender, EventArgs e)
{
AlertDialog.Builder builder = new AlertDialog.Builder(Activity);
builder.SetTitle(Resource.String.ajouter_categorie);
LayoutInflater inflater = Activity.LayoutInflater;
View v = inflater.Inflate(Resource.Layout.alertdialog_ajouter_categorie_article_configuration, null);
TextView nom = v.FindViewById<TextView>(Resource.Id.ed_nom_ajouter_categorie_fragment_article_configuration);
builder.SetPositiveButton(Resource.String.ajouter, delegate
{
if (nom.Text.Length > 0)
{
CategoriesArticlesConfig c = CategoriesArticlesConfig.New();
c.Nom = nom.Text;
c.Save();
c = CategoriesArticlesConfig.ReadFirst("CategorieArticlesConfigID=" + c.CategorieArticlesConfigID);
listCatgArticles.Add(c);
adapterCatConfig.NotifyDataSetChanged();
}
});
builder.SetNegativeButton(Resource.String.annuler, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.SetView(v);
builder.Show();
}
}
}
ListCategorieArticlesConfigAdapter.cs
namespace SymaMobile.Droid.Adapters
{
class ListCategorieArticlesConfigAdapter : BaseAdapter
{
private List<CategoriesArticlesConfig> list;
private int indice;
private Activity Activity;
public ListCategorieArticlesConfigAdapter(Android.App.Activity Activity, List<CategoriesArticlesConfig> list)
: base()
{
this.Activity = Activity;
this.list = list;
}
public override int Count
{
get { return list.Count; }
}
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return list[position].CategorieArticlesConfigID;
}
public override Android.Views.View GetView(int position, Android.Views.View convertView, Android.Views.ViewGroup parent)
{
var view = (convertView ??
Activity.LayoutInflater.Inflate(
Resource.Layout.list_item_categories_articles_configuration,
parent,
false)) as LinearLayout;
var nom = view.FindViewById(Resource.Id.tv_nom_list_item_categories_articles_configuration) as TextView;
var modify = view.FindViewById(Resource.Id.bt_modify_list_categories_articles_configuration) as Button;
var delete = view.FindViewById(Resource.Id.bt_delete_list_categories_articles_configuration) as Button;
modify.Click += modify_Click;
indice = position;
delete.Click +=delete_Click;
nom.Text = list[position].Nom;
return view;
}
void delete_Click(object sender, EventArgs e)
{
Android.App.AlertDialog.Builder builder = new Android.App.AlertDialog.Builder(Activity);
builder.SetMessage(Resource.String.msg_supprimer);
builder.SetPositiveButton(Resource.String.oui, delegate
{
CSDatabase.ExecuteNonQuery("DELETE FROM CategoriesArticlesConfig WHERE CategorieArticlesConfigID=" + list[indice].CategorieArticlesConfigID);
list.RemoveAt(indice);
NotifyDataSetChanged();
});
builder.SetNegativeButton(Resource.String.non, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.Show();
}
void modify_Click(object sender, EventArgs e)
{
AlertDialog.Builder builder = new AlertDialog.Builder(Activity);
builder.SetTitle(Resource.String.modifier_categorie);
LayoutInflater inflater = Activity.LayoutInflater;
View v = inflater.Inflate(Resource.Layout.alertdialog_ajouter_categorie_article_configuration, null);
TextView _nom = v.FindViewById<TextView>(Resource.Id.ed_nom_ajouter_categorie_fragment_article_configuration);
_nom.Text = list[indice].Nom;
builder.SetNegativeButton(Resource.String.annuler, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.SetPositiveButton(Resource.String.modifier, delegate {
CategoriesArticlesConfig c = CategoriesArticlesConfig.ReadFirst("CategorieArticlesConfigID=" + list[indice].CategorieArticlesConfigID);
if (c != null)
{
c.Nom = _nom.Text;
c.Save();
}
list[indice].Nom = _nom.Text;
NotifyDataSetChanged();
});
builder.SetView(v);
builder.Show();
}
}
}
ListArticleConfigAdapter
namespace SymaMobile.Droid.Adapters
{
class ListArticleConfigAdapter : BaseAdapter
{
Activity context;
private List<ArticlesConfig> liste;
private int indice;
public ListArticleConfigAdapter(Activity context, List<ArticlesConfig> liste)
: base()
{
this.context = context;
this.liste = liste;
}
public override int Count
{
get { return liste.Count+1; }
}
public override long GetItemId(int position)
{
return position;
}
public override Android.Views.View GetView(int position, View convertView, ViewGroup parent)
{
var view = (convertView ??
context.LayoutInflater.Inflate(
Resource.Layout.list_item_article_configuration,
parent,
false)) as LinearLayout;
var image = view.FindViewById(Resource.Id.iv_list_item_article_configuration) as ImageView;
var nom = view.FindViewById(Resource.Id.tv_nom_article_list_item_article_configuration) as TextView;
var supprimer = view.FindViewById(Resource.Id.bt_delete_list_item_article_configuration) as Button;
Android.Util.Log.Error("Position : ", ""+position+" List Count : "+liste.Count);
if (position < liste.Count)
{
nom.Text = liste[position].Article.Libelle;
image.SetImageBitmap(Tools.ImageTools.StringToBitMap(liste[position].Article.Image));
}
else
{
nom.Text = context.Resources.GetString(Resource.String.ajouter);
image.SetImageResource(Resource.Drawable.add128x128);
supprimer.Visibility = ViewStates.Invisible;
}
indice = position;
supprimer.Click += supprimer_Click;
return view;
}
void supprimer_Click(object sender, EventArgs e)
{
Android.App.AlertDialog.Builder builder = new Android.App.AlertDialog.Builder(context);
builder.SetMessage(Resource.String.msg_supprimer);
builder.SetPositiveButton(Resource.String.oui, delegate
{
CSDatabase.ExecuteNonQuery("DELETE FROM ArticlesConfig WHERE ArticlesConfigID=" + liste[indice].ArticlesConfigID);
liste.RemoveAt(indice);
NotifyDataSetChanged();
});
builder.SetNegativeButton(Resource.String.non, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.Show();
}
public override Java.Lang.Object GetItem(int position)
{
return position;
}
}
}
thank you in advance
u have need to do this code on next activity onbackpress or
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
progressDialog.dismiss();
}
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
System.exit(0);
}
In ListCategorieArticlesConfigAdapter.GetView (), you're adding new event handlers for modify and delete, even when the view has been recycled, which means it will already have those handlers. Convert your convertView ?? check into an if/else check and do not reinitialise the handlers when convertView != null.
Modified your adapter to use view holder pattern. This should solve your issues.
class ListCategorieArticlesConfigAdapter : BaseAdapter
{
private List<CategoriesArticlesConfig> list;
private Activity Activity;
public ListCategorieArticlesConfigAdapter(Android.App.Activity Activity, List<CategoriesArticlesConfig> list)
: base()
{
this.Activity = Activity;
this.list = list;
}
public override int Count
{
get { return list.Count; }
}
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return list[position].CategorieArticlesConfigID;
}
public override Android.Views.View GetView(int position, Android.Views.View convertView,
Android.Views.ViewGroup parent)
{
ViewHolder vh;
var view = convertView;
if (view == null)
{
view = Activity.LayoutInflater.Inflate(Resource.Layout.list_item_categories_articles_configuration,
false);
vh = new ViewHolder(list);
vh.Initialize(view);
view.Tag = vh;
}
vh = view.Tag as ViewHolder;
vh.Bind(position);
return view;
}
private class ViewHolder : Java.Lang.Object
{
private TextView _nom;
private Button _modify;
private Button _delete;
private List<CategoriesArticlesConfig> _list;
public ViewHolder(List<CategoriesArticlesConfig> list)
{
_list = list;
}
public void Initialize(View view)
{
_nom = view.FindViewById<TextView>(Resource.Id.tv_nom_list_item_categories_articles_configuration);
_modify = view.FindViewById<Button>(Resource.Id.bt_modify_list_categories_articles_configuration);
_delete = view.FindViewById<Button>(Resource.Id.bt_delete_list_categories_articles_configuration);
}
public void Bind(int position)
{
_modify.Tag = position;
_modify.Click += modify_Click;
_delete.Click += delete_Click;
_nom.Text = _list[position].Nom;
}
void delete_Click(object sender, EventArgs e)
{
var indice = (int)(((View)sender).Tag);
Android.App.AlertDialog.Builder builder = new Android.App.AlertDialog.Builder(Activity);
builder.SetMessage(Resource.String.msg_supprimer);
builder.SetPositiveButton(Resource.String.oui, delegate
{
CSDatabase.ExecuteNonQuery("DELETE FROM CategoriesArticlesConfig WHERE CategorieArticlesConfigID=" + list[indice].CategorieArticlesConfigID);
list.RemoveAt(indice);
NotifyDataSetChanged();
});
builder.SetNegativeButton(Resource.String.non, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.Show();
}
void modify_Click(object sender, EventArgs e)
{
var indice = (int)(((View)sender).Tag);
AlertDialog.Builder builder = new AlertDialog.Builder(Activity);
builder.SetTitle(Resource.String.modifier_categorie);
LayoutInflater inflater = Activity.LayoutInflater;
View v = inflater.Inflate(Resource.Layout.alertdialog_ajouter_categorie_article_configuration, null);
TextView _nom = v.FindViewById<TextView>(Resource.Id.ed_nom_ajouter_categorie_fragment_article_configuration);
_nom.Text = _list[indice].Nom;
builder.SetNegativeButton(Resource.String.annuler, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.SetPositiveButton(Resource.String.modifier, delegate
{
CategoriesArticlesConfig c = CategoriesArticlesConfig.ReadFirst("CategorieArticlesConfigID=" + list[indice].CategorieArticlesConfigID);
if (c != null)
{
c.Nom = _nom.Text;
c.Save();
}
_list[indice].Nom = _nom.Text;
NotifyDataSetChanged();
});
builder.SetView(v);
builder.Show();
}
}
}
I find a solution. Is not the best but it work fine.
Firstly, I declare a boolean variable :
bool entree;
and in GetView method :
if ((position == 0 && !entree) || (position != 0 && entree))
{
...
entree = true;
}
that's all.
I added a property to my webpart main.cs
public override ToolPart[] GetToolParts()
{
ToolPart[] toolparts = new ToolPart[4];
WebPartToolPart wptp = new WebPartToolPart();
CustomPropertyToolPart custom = new CustomPropertyToolPart();
CalendarToolPart datumswahl = new CalendarToolPart("Coutdown Datum Wahl");
toolparts[0] = datumswahl;
toolparts[1] = custom;
toolparts[2] = wptp;
return toolparts;
}
I then built this class toolpartClass.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
namespace Custom_Toolpart.toolparttest
{
class CalendarToolPart : ToolPart
{
public Calendar datumWahl = null;
private void CalendarToolPart_Init(object sender, EventArgs e)
{
datumWahl = new Calendar();
datumWahl.ID = "datumWahl";
SyncChanges();
}
public CalendarToolPart(string strTitle)
{
this.Title = strTitle;
this.Init += new EventHandler(CalendarToolPart_Init);
}
public override void ApplyChanges()
{
//base.ApplyChanges();
EnsureChildControls();
}
public override void SyncChanges()
{
EnsureChildControls();
}
protected override void CreateChildControls()
{
base.CreateChildControls();
Controls.Add(datumWahl);
this.ChildControlsCreated = true; //?
}
}
My problem is, that I can't use/call the calendar out of the toolpartClass.cs
What I want to do is, that the selected date is given to the "toolpartUserControl.ascx.cs" (the code behind) where I want to use the date and display a result in the toolpart.ascx
It is no problem to use the "normal" added webpart properties in the toolpartUserControl.ascx.cs, like here
namespace Custom_Toolpart.toolparttest
{
public partial class toolparttestUserControl : UserControl
{
public toolparttest WebPart { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
DateTime datumAuswahl1 = DateTime.Parse(Calendar1.SelectedDate.ToString("MM") + "/" + Calendar1.SelectedDate.ToString("dd") + "/" + Calendar1.SelectedDate.ToString("yyyy"));
DateTime datumJetzt = DateTime.Now;
TimeSpan mySpan = new TimeSpan(-24, 0, 0);
datumJetzt = datumJetzt.Add(mySpan);
TimeSpan laufzeit = datumAuswahl1 - datumJetzt;
string ausgabeLaufzeit = string.Format("{0}", laufzeit.Days);
int i = Convert.ToInt32(ausgabeLaufzeit);
if (i >= 2)
{
lbl_6.Text = "Tage";
}
if (i == 1)
{
lbl_6.Text = "Tag";
}
if (i == 0)
{
lbl_6.Text = "Tage";
lbl_hide.Visible = true;
lbl_1.Visible = false;
lbl_2.Visible = false;
lbl_3.Visible = false;
lbl_6.Visible = false;
}
lbl_2.Text = ausgabeLaufzeit;
if (this.WebPart != null && this.WebPart.CountDown_Text != null)
{
lbl_1.Text = this.WebPart.CountDown_Text.ToString();
}
if (this.WebPart != null && this.WebPart.Event_Text != null)
{
lbl_3.Text = this.WebPart.Event_Text.ToString();
}
if (this.WebPart != null && this.WebPart.Header_Text != null)
{
lbl_4.Text = this.WebPart.Header_Text.ToString();
}
if (this.WebPart != null && this.WebPart.Main_Text != null)
{
lbl_5.Text = this.WebPart.Main_Text.ToString();
}
if (this.WebPart != null && this.WebPart.Link_Edit != null)
{
hyplnk_1.NavigateUrl = this.WebPart.Link_Edit.ToString();
}
if (this.WebPart != null && this.WebPart.Ablauf_Text != null)
{
lbl_hide.Text = this.WebPart.Ablauf_Text.ToString();
}
}
}
}
How can I send the selected date to the toolpartUserControl.ascx.cs class?