Custom Pull-To-Refresh not working on iOS - c#

I am following James Montemagno's tutorial to add pull-to-refresh support for my Layouts, it works perfect on Android but iOS generates the error below when I navigate to the same page as Android does.
System.InvalidCastException: < Timeout exceeded getting exception details >
The page I am trying to display is a simple StackLayout, which again works perfectly on Android.
This is my iOS renderer class from the tutorial
[assembly: ExportRenderer(typeof(RefreshableLayout), typeof(RefreshableLayoutiOS))]
namespace SocialNetwork.iOS.Renderers
{
[Preserve(AllMembers = true)]
public class RefreshableLayoutiOS : ViewRenderer<RefreshableLayout, UIView>
{
public async static void Init()
{
var temp = DateTime.Now;
}
UIRefreshControl refreshControl;
protected override void OnElementChanged(ElementChangedEventArgs<RefreshableLayout> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
return;
refreshControl = new UIRefreshControl();
refreshControl.ValueChanged += OnRefresh;
try
{
TryInsertRefresh(this);
}
catch (Exception ex)
{
Debug.WriteLine("View is not supported in PullToRefreshLayout: " + ex);
}
UpdateColors();
UpdateIsRefreshing();
UpdateIsSwipeToRefreshEnabled();
}
bool set;
nfloat origininalY;
bool TryOffsetRefresh(UIView view, bool refreshing, int index = 0)
{
if (view is UITableView)
{
var uiTableView = view as UITableView;
if (!set)
{
origininalY = uiTableView.ContentOffset.Y;
set = true;
}
if (refreshing)
uiTableView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY - refreshControl.Frame.Size.Height), true);
else
uiTableView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY), true);
return true;
}
if (view is UICollectionView)
{
var uiCollectionView = view as UICollectionView;
if (!set)
{
origininalY = uiCollectionView.ContentOffset.Y;
set = true;
}
if (refreshing)
uiCollectionView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY - refreshControl.Frame.Size.Height), true);
else
uiCollectionView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY), true);
return true;
}
if (view is UIWebView)
{
//can't do anything
return true;
}
if (view is UIScrollView)
{
var uiScrollView = view as UIScrollView;
if (!set)
{
origininalY = uiScrollView.ContentOffset.Y;
set = true;
}
if (refreshing)
uiScrollView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY - refreshControl.Frame.Size.Height), true);
else
uiScrollView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY), true);
return true;
}
if (view.Subviews == null)
return false;
for (int i = 0; i < view.Subviews.Length; i++)
{
var control = view.Subviews[i];
if (TryOffsetRefresh(control, refreshing, i))
return true;
}
return false;
}
bool TryInsertRefresh(UIView view, int index = 0)
{
if (view is UITableView)
{
var uiTableView = view as UITableView;
uiTableView = view as UITableView;
view.InsertSubview(refreshControl, index);
return true;
}
if (view is UICollectionView)
{
var uiCollectionView = view as UICollectionView;
uiCollectionView = view as UICollectionView;
view.InsertSubview(refreshControl, index);
return true;
}
if (view is UIWebView)
{
var uiWebView = view as UIWebView;
uiWebView.ScrollView.InsertSubview(refreshControl, index);
return true;
}
if (view is UIScrollView)
{
var uiScrollView = view as UIScrollView;
view.InsertSubview(refreshControl, index);
uiScrollView.AlwaysBounceVertical = true;
return true;
}
if (view.Subviews == null)
return false;
for (int i = 0; i < view.Subviews.Length; i++)
{
var control = view.Subviews[i];
if (TryInsertRefresh(control, i))
return true;
}
return false;
}
BindableProperty rendererProperty;
BindableProperty RendererProperty
{
get
{
if (rendererProperty != null)
return rendererProperty;
var type = Type.GetType("Xamarin.Forms.Platform.iOS.Platform, Xamarin.Forms.Platform.iOS");
var prop = type.GetField("RendererProperty");
var val = prop.GetValue(null);
rendererProperty = val as BindableProperty;
return rendererProperty;
}
}
void UpdateColors()
{
if (RefreshView == null)
return;
if (RefreshView.RefreshColor != Color.Default)
refreshControl.TintColor = RefreshView.RefreshColor.ToUIColor();
if (RefreshView.RefreshBackgroundColor != Color.Default)
refreshControl.BackgroundColor = RefreshView.RefreshBackgroundColor.ToUIColor();
}
void UpdateIsRefreshing()
{
IsRefreshing = RefreshView.IsRefreshing;
}
void UpdateIsSwipeToRefreshEnabled()
{
refreshControl.Enabled = RefreshView.IsPullToRefreshEnabled;
}
public RefreshableLayout RefreshView
{
get { return Element; }
}
bool isRefreshing;
public bool IsRefreshing
{
get { return isRefreshing; }
set
{
bool changed = IsRefreshing != value;
isRefreshing = value;
if (isRefreshing)
refreshControl.BeginRefreshing();
else
refreshControl.EndRefreshing();
if (changed)
TryOffsetRefresh(this, IsRefreshing);
}
}
void OnRefresh(object sender, EventArgs e)
{
if (RefreshView?.RefreshCommand?.CanExecute(RefreshView?.RefreshCommandParameter) ?? false)
{
RefreshView.RefreshCommand.Execute(RefreshView?.RefreshCommandParameter);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == RefreshableLayout.IsPullToRefreshEnabledProperty.PropertyName)
UpdateIsSwipeToRefreshEnabled();
else if (e.PropertyName == RefreshableLayout.IsRefreshingProperty.PropertyName)
UpdateIsRefreshing();
else if (e.PropertyName == RefreshableLayout.RefreshColorProperty.PropertyName)
UpdateColors();
else if (e.PropertyName == RefreshableLayout.RefreshBackgroundColorProperty.PropertyName)
UpdateColors();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (refreshControl != null)
{
refreshControl.ValueChanged -= OnRefresh;
}
}
}
}
I got the code this tutorial and this GitHub
Edit:
XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SocialNetwork.TestScrollPage" xmlns:local="clr-namespace:SocialNetwork.Renderers">
<ContentPage.Content>
<StackLayout>
<local:RefreshableLayout x:Name="RefreshableLayout" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout>
</StackLayout>
</local:RefreshableLayout>
</StackLayout>
</ContentPage.Content>
CS:
public partial class TestScrollPage : ContentPage
{
public TestScrollPage ()
{
InitializeComponent ();
RefreshableLayout.RefreshCommand = new Command(() => RefreshPage());
}
public void RefreshPage()
{
RefreshableLayout.IsRefreshing = false;
DisplayAlert("ok", "ok", "ok");
}
}
And I navigate to the page using Detail = new TestScrollPage();
Edit 2:
public partial class RefreshableLayout : ContentView
{
public static readonly BindableProperty IsRefreshingProperty =
BindableProperty.Create(nameof(IsRefreshing), typeof(bool), typeof(RefreshableLayout), false);
/// <summary>
/// Gets or sets a value indicating whether this instance is refreshing.
/// </summary>
/// <value><c>true</c> if this instance is refreshing; otherwise, <c>false</c>.</value>
public bool IsRefreshing
{
get { return (bool)GetValue(IsRefreshingProperty); }
set
{
if ((bool)GetValue(IsRefreshingProperty) == value)
OnPropertyChanged(nameof(IsRefreshing));
SetValue(IsRefreshingProperty, value);
}
}
/// <summary>
/// The is pull to refresh enabled property.
/// </summary>
public static readonly BindableProperty IsPullToRefreshEnabledProperty =
BindableProperty.Create(nameof(IsPullToRefreshEnabled), typeof(bool), typeof(RefreshableLayout), true);
/// <summary>
/// Gets or sets a value indicating whether this instance is pull to refresh enabled.
/// </summary>
/// <value><c>true</c> if this instance is pull to refresh enabled; otherwise, <c>false</c>.</value>
public bool IsPullToRefreshEnabled
{
get { return (bool)GetValue(IsPullToRefreshEnabledProperty); }
set { SetValue(IsPullToRefreshEnabledProperty, value); }
}
/// <summary>
/// The refresh command property.
/// </summary>
public static readonly BindableProperty RefreshCommandProperty =
BindableProperty.Create(nameof(RefreshCommand), typeof(ICommand), typeof(RefreshableLayout));
/// <summary>
/// Gets or sets the refresh command.
/// </summary>
/// <value>The refresh command.</value>
public ICommand RefreshCommand
{
get { return (ICommand)GetValue(RefreshCommandProperty); }
set { SetValue(RefreshCommandProperty, value); }
}
/// <summary>
/// Gets the Refresh command
/// </summary>
public static readonly BindableProperty RefreshCommandParameterProperty =
BindableProperty.Create(nameof(RefreshCommandParameter),
typeof(object),
typeof(RefreshableLayout),
null,
propertyChanged: (bindable, oldvalue, newvalue) => ((RefreshableLayout)bindable).RefreshCommandCanExecuteChanged(bindable, EventArgs.Empty));
/// <summary>
/// Gets or sets the Refresh command parameter
/// </summary>
public object RefreshCommandParameter
{
get { return GetValue(RefreshCommandParameterProperty); }
set { SetValue(RefreshCommandParameterProperty, value); }
}
/// <summary>
/// Executes if enabled or not based on can execute changed
/// </summary>
/// <param name="sender"></param>
/// <param name="eventArgs"></param>
void RefreshCommandCanExecuteChanged(object sender, EventArgs eventArgs)
{
ICommand cmd = RefreshCommand;
if (cmd != null)
IsEnabled = cmd.CanExecute(RefreshCommandParameter);
}
/// <summary>
/// Color property of refresh spinner color
/// </summary>
public static readonly BindableProperty RefreshColorProperty =
BindableProperty.Create(nameof(RefreshColor), typeof(Color), typeof(RefreshableLayout), Color.Default);
/// <summary>
/// Refresh color
/// </summary>
public Color RefreshColor
{
get { return (Color)GetValue(RefreshColorProperty); }
set { SetValue(RefreshColorProperty, value); }
}
/// <summary>
/// Color property of refresh background color
/// </summary>
public static readonly BindableProperty RefreshBackgroundColorProperty =
BindableProperty.Create(nameof(RefreshBackgroundColor), typeof(Color), typeof(RefreshableLayout), Color.Default);
/// <summary>
/// Refresh background color
/// </summary>
public Color RefreshBackgroundColor
{
get { return (Color)GetValue(RefreshBackgroundColorProperty); }
set { SetValue(RefreshBackgroundColorProperty, value); }
}
/// <param name="widthConstraint">The available width for the element to use.</param>
/// <param name="heightConstraint">The available height for the element to use.</param>
/// <summary>
/// Optimization as we can get the size here of our content all in DIP
/// </summary>
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
if (Content == null)
return new SizeRequest(new Size(100, 100));
return base.OnMeasure(widthConstraint, heightConstraint);
}
}

Please read this documentation about Xamarin Liver Player. It declares the limitations:
Custom Renderers are not supported for Xamarin Forms.
Also there are some other limitions or issues when you use Xamarin Liver Player. So I recommend you to use simulators or a real physical device to test your project.
If you don't have a Mac. You can also try to download an Enterprise Visual Studio to let the simulators mapping to Windows.

Related

.NET MAUI: Customize datepicker so that it opens when clicking on an icon

I want to display a calendar icon, then when the user taps/clicks the calendar icon, the date picker opens up. Is there a way to customize the date picker to implement this functionality?
Do I need a custom handler?
I tried calling Focus() on the date picker, but it didn't open up.
<HorizontalStackLayout>
<DatePicker x:Name="MyDatePicker" MinimumDate="01/01/2022"
MaximumDate="12/31/2022"
Date="06/21/2022" />
<ImageButton Source="calendar.svg" Clicked="ImageButton_Clicked"></ImageButton>
</HorizontalStackLayout>
private void ImageButton_Clicked(object sender, EventArgs e)
{
MyDatePicker.Focus();
}
Note: The icon is from: https://feathericons.com/?query=calendar
Utilizing Maui.FreakyControls and a custom behavior resulted in this solution for Android.
It does require a custom handler to implement successfully.
MauiProgram.cs
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureMauiHandlers(handlers =>
{
handlers.AddCustomHandlers();
});
return builder.Build();
}
Shared/Extensions/Extensions.cs
#if ANDROID
using Microsoft.Maui.Controls.Compatibility.Platform.Android;
using static Microsoft.Maui.ApplicationModel.Platform;
using NativeImage = Android.Graphics.Bitmap;
#endif
public static void ExecuteCommandIfAvailable(this ICommand command, object parameter = null)
{
if (command?.CanExecute(parameter) == true)
{
command.Execute(parameter);
}
}
public static void AddCustomHandlers(this IMauiHandlersCollection handlers)
{
#if ANDROID
handlers.AddHandler(typeof(Maui.CustomControls.CustomDatePicker), typeof(CustomDatePickerHandler));
#endif
}
#if ANDROID
public static async Task<NativeImage> ToNativeImageSourceAsync(this ImageSource source)
{
var handler = GetHandler(source);
var returnValue = (NativeImage)null;
returnValue = await handler.LoadImageAsync(source, CurrentActivity);
return returnValue;
}
private static IImageSourceHandler GetHandler(this ImageSource source)
{
//Image source handler to return
IImageSourceHandler returnValue = null;
//check the specific source type and return the correct image source handler
switch (source)
{
case UriImageSource:
returnValue = new ImageLoaderSourceHandler();
break;
case FileImageSource:
returnValue = new FileImageSourceHandler();
break;
case StreamImageSource:
returnValue = new StreamImagesourceHandler();
break;
case FontImageSource:
returnValue = new FontImageSourceHandler();
break;
}
return returnValue;
}
#endif
Android/NativeControls/Helpers/DrawableHandlerCallback.cs
public class DrawableHandlerCallback : IDrawableClickListener
{
private readonly IDrawableImageView view;
private readonly Action showPicker;
public DrawableHandlerCallback(IDrawableImageView view, Action showPicker)
{
this.view = view;
this.showPicker = showPicker;
}
public void OnClick(DrawablePosition target)
{
switch (target)
{
case DrawablePosition.Left:
case DrawablePosition.Right:
view.ImageTappedHandler.Invoke(this.showPicker, null);
view.ImageCommand?.ExecuteCommandIfAvailable(view.ImageCommandParameter);
break;
}
}
}
Android/NativeControls/Helpers/DrawablePosition.cs
public enum DrawablePosition
{
Top,
Bottom,
Left,
Right
};
Android/NativeControls/Helpers/IDrawableClickListener.cs
public interface IDrawableClickListener
{
public void OnClick(DrawablePosition target);
}
Android/NativeControls/CustomMauiDatePicker.cs
public class CustomMauiDatePicker : MauiDatePicker
{
private Drawable drawableRight;
private Drawable drawableLeft;
private Drawable drawableTop;
private Drawable drawableBottom;
int actionX, actionY;
private IDrawableClickListener clickListener;
public CustomMauiDatePicker(Context context) : base(context)
{
}
public CustomMauiDatePicker(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public CustomMauiDatePicker(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
}
public override void SetCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top,
Drawable right, Drawable bottom)
{
if (left != null)
{
drawableLeft = left;
}
if (right != null)
{
drawableRight = right;
}
if (top != null)
{
drawableTop = top;
}
if (bottom != null)
{
drawableBottom = bottom;
}
base.SetCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
}
public override bool OnTouchEvent(MotionEvent e)
{
Rect bounds;
if (e.Action == MotionEventActions.Down)
{
actionX = (int)e.GetX();
actionY = (int)e.GetY();
if (drawableBottom != null
&& drawableBottom.Bounds.Contains(actionX, actionY))
{
clickListener.OnClick(DrawablePosition.Bottom);
return base.OnTouchEvent(e);
}
if (drawableTop != null
&& drawableTop.Bounds.Contains(actionX, actionY))
{
clickListener.OnClick(DrawablePosition.Top);
return base.OnTouchEvent(e);
}
// this works for left since container shares 0,0 origin with bounds
if (drawableLeft != null)
{
bounds = null;
bounds = drawableLeft.Bounds;
int x, y;
int extraTapArea = (int)(13 * Resources.DisplayMetrics.Density + 0.5);
x = actionX;
y = actionY;
if (!bounds.Contains(actionX, actionY))
{
// Gives the +20 area for tapping. /
x = (int)(actionX - extraTapArea);
y = (int)(actionY - extraTapArea);
if (x <= 0)
x = actionX;
if (y <= 0)
y = actionY;
// Creates square from the smallest value /
if (x < y)
{
y = x;
}
}
if (bounds.Contains(x, y) && clickListener != null)
{
clickListener.OnClick(DrawablePosition.Left);
e.Action = (MotionEventActions.Cancel);
return false;
}
}
if (drawableRight != null)
{
bounds = null;
bounds = drawableRight.Bounds;
int x, y;
int extraTapArea = 13;
//
// IF USER CLICKS JUST OUT SIDE THE RECTANGLE OF THE DRAWABLE
// THAN ADD X AND SUBTRACT THE Y WITH SOME VALUE SO THAT AFTER
// CALCULATING X AND Y CO-ORDINATE LIES INTO THE DRAWBABLE
// BOUND. - this process help to increase the tappable area of
// the rectangle.
//
x = (int)(actionX + extraTapArea);
y = (int)(actionY - extraTapArea);
//Since this is right drawable subtract the value of x from the width
// of view. so that width - tappedarea will result in x co-ordinate in drawable bound.
//
x = Width - x;
//x can be negative if user taps at x co-ordinate just near the width.
// e.g views width = 300 and user taps 290. Then as per previous calculation
// 290 + 13 = 303. So subtract X from getWidth() will result in negative value.
// So to avoid this add the value previous added when x goes negative.
//
if (x <= 0)
{
x += extraTapArea;
}
// If result after calculating for extra tappable area is negative.
// assign the original value so that after subtracting
// extratapping area value doesn't go into negative value.
//
if (y <= 0)
y = actionY;
//If drawble bounds contains the x and y points then move ahead./
if (bounds.Contains(x, y) && clickListener != null)
{
clickListener
.OnClick(DrawablePosition.Right);
e.Action = (MotionEventActions.Cancel);
return false;
}
return base.OnTouchEvent(e);
}
}
return base.OnTouchEvent(e);
}
protected override void JavaFinalize()
{
drawableRight = null;
drawableBottom = null;
drawableLeft = null;
drawableTop = null;
base.JavaFinalize();
}
public void SetDrawableClickListener(IDrawableClickListener listener)
{
this.clickListener = listener;
}
}
Platforms/Android/CustomDatePickerHandler.android.cs
public partial class CustomDatePickerHandler
{
DatePickerDialog? _dialog;
protected override MauiDatePicker CreatePlatformView()
{
var mauiDatePicker = new CustomMauiDatePicker(Context)
{
ShowPicker = ShowPickerDialog,
HidePicker = HidePickerDialog
};
var date = VirtualView?.Date;
if (date != null)
_dialog = CreateDatePickerDialog(date.Value.Year, date.Value.Month, date.Value.Day);
var colorStateList = ColorStateList.ValueOf(Android.Graphics.Color.Transparent);
ViewCompat.SetBackgroundTintList(mauiDatePicker, colorStateList);
return mauiDatePicker;
}
protected override void DisconnectHandler(MauiDatePicker platformView)
{
base.DisconnectHandler(platformView);
if (_dialog != null)
{
_dialog.Hide();
_dialog.Dispose();
_dialog = null;
}
}
internal DatePickerDialog? DatePickerDialog { get { return _dialog; } }
internal async Task HandleAndAlignImageSourceAsync(Maui.CustomControls.CustomDatePicker entry)
{
var imageBitmap = await entry.ImageSource?.ToNativeImageSourceAsync();
if (imageBitmap != null)
{
var bitmapDrawable = new BitmapDrawable(Platform.CurrentActivity?.Resources,
Bitmap.CreateScaledBitmap(imageBitmap, entry.ImageWidth * 2, entry.ImageHeight * 2, true));
var customDatePicker = PlatformView as CustomMauiDatePicker;
customDatePicker.SetDrawableClickListener(new DrawableHandlerCallback(entry, customDatePicker.ShowPicker));
switch (entry.ImageAlignment)
{
case ImageAlignment.Left:
customDatePicker.SetCompoundDrawablesWithIntrinsicBounds(bitmapDrawable, null, null, null);
break;
case ImageAlignment.Right:
customDatePicker.SetCompoundDrawablesWithIntrinsicBounds(null, null, bitmapDrawable, null);
break;
}
}
PlatformView.CompoundDrawablePadding = entry.ImagePadding;
}
void ShowPickerDialog()
{
if (VirtualView == null)
return;
if (_dialog != null && _dialog.IsShowing)
return;
var date = VirtualView.Date;
ShowPickerDialog(date.Year, date.Month - 1, date.Day);
}
void ShowPickerDialog(int year, int month, int day)
{
if (_dialog == null)
_dialog = CreateDatePickerDialog(year, month, day);
else
{
EventHandler? setDateLater = null;
setDateLater = (sender, e) => { _dialog!.UpdateDate(year, month, day); _dialog.ShowEvent -= setDateLater; };
_dialog.ShowEvent += setDateLater;
}
_dialog.Show();
}
void HidePickerDialog()
{
_dialog?.Hide();
}
}
Shared/Controls/IDrawableImageView.cs
public interface IDrawableImageView
{
public EventHandler ImageTappedHandler
{
get;
}
public object ImageCommandParameter
{
get;
}
public ICommand ImageCommand
{
get;
}
public int ImagePadding
{
get;
}
public int ImageWidth
{
get;
}
public int ImageHeight
{
get;
}
public ImageSource ImageSource
{
get;
}
public ImageAlignment ImageAlignment
{
get;
}
}
Shared/CustomDatePicker/CustomDatePicker.cs
public class CustomDatePicker : DatePicker, IDrawableImageView
{
private EventHandler imageTapped;
public EventHandler ImageTappedHandler
{
get { return imageTapped; }
}
public event EventHandler ImageTapped
{
add
{
imageTapped += value;
}
remove
{
imageTapped -= value;
}
}
public static readonly BindableProperty ImageSourceProperty = BindableProperty.Create(
nameof(Image),
typeof(ImageSource),
typeof(CustomDatePicker),
default(ImageSource));
public static readonly BindableProperty ImageHeightProperty = BindableProperty.Create(
nameof(ImageHeight),
typeof(int),
typeof(CustomDatePicker),
25);
public static readonly BindableProperty ImageWidthProperty = BindableProperty.Create(
nameof(ImageWidth),
typeof(int),
typeof(CustomDatePicker),
25);
public static readonly BindableProperty ImageAlignmentProperty = BindableProperty.Create(
nameof(ImageAlignment),
typeof(ImageAlignment),
typeof(CustomDatePicker),
ImageAlignment.Right);
public static readonly BindableProperty ImagePaddingProperty = BindableProperty.Create(
nameof(ImagePadding),
typeof(int),
typeof(CustomDatePicker),
5);
public static readonly BindableProperty ImageCommandProperty = BindableProperty.Create(
nameof(ImagePadding),
typeof(ICommand),
typeof(CustomDatePicker),
default(ICommand));
public static readonly BindableProperty ImageCommandParameterProperty = BindableProperty.Create(
nameof(ImageCommandParameter),
typeof(object),
typeof(CustomDatePicker),
default(object));
/// <summary>
/// Command parameter for your Image tap command
/// </summary>
public object ImageCommandParameter
{
get => GetValue(ImageCommandParameterProperty);
set => SetValue(ImageCommandParameterProperty, value);
}
/// <summary>
/// <see cref="ImageCommand"/> of type <see cref="ICommand"/> that you can use to bind with your Image that you added to your control's ViewPort
/// </summary>
public ICommand ImageCommand
{
get => (ICommand)GetValue(ImageCommandProperty);
set => SetValue(ImageCommandProperty, value);
}
/// <summary>
/// Padding of the Image as <see cref="int"/> that you added to the ViewPort
/// </summary>
public int ImagePadding
{
get => (int)GetValue(ImagePaddingProperty);
set => SetValue(ImagePaddingProperty, value);
}
/// <summary>
/// Width of the Image in your ViewPort
/// </summary>
public int ImageWidth
{
get => (int)GetValue(ImageWidthProperty);
set => SetValue(ImageWidthProperty, value);
}
/// <summary>
/// Height of the Image in your ViewPort
/// </summary>
public int ImageHeight
{
get => (int)GetValue(ImageHeightProperty);
set => SetValue(ImageHeightProperty, value);
}
/// <summary>
/// An <see cref="ImageSource"/> that you want to add to your ViewPort
/// </summary>
public ImageSource ImageSource
{
get => (ImageSource)GetValue(ImageSourceProperty);
set => SetValue(ImageSourceProperty, value);
}
/// <summary>
/// <see cref="ImageAlignment"/> for your Image's ViewPort, By default set to Right.
/// </summary>
public ImageAlignment ImageAlignment
{
get => (ImageAlignment)GetValue(ImageAlignmentProperty);
set => SetValue(ImageAlignmentProperty, value);
}
}
Shared/CustomDatePickerHandler/CustomDatePickerHandler.cs
#if ANDROID
public partial class CustomDatePickerHandler : DatePickerHandler
{
public CustomDatePickerHandler()
{
Mapper.AppendToMapping("CustomDatePickerCustomization", MapDatePicker);
}
private void MapDatePicker(IDatePickerHandler datePickerHandler, IDatePicker datePicker)
{
if (datePicker is Maui.CustomControls.CustomDatePicker customDatePicker &&
datePickerHandler is CustomDatePickerHandler customDatePickerHandler)
{
if (customDatePicker.ImageSource != default(ImageSource))
{
customDatePickerHandler.HandleAndAlignImageSourceAsync(customDatePicker).RunConcurrently();
}
}
}
}
#endif
Shared/Enums/ImageAlignment.cs
public enum ImageAlignment
{
/// <summary>
/// Aligns your control to the left view port of the view.
/// </summary>
Left,
/// <summary>
/// Aligns your control to the right view port of the view.
/// </summary>
Right
}
Shared/Extensions/TaskExtensions.cs
public static class TaskExtensions
{
public static void RunConcurrently(this Task task)
{
if (task == null)
throw new ArgumentNullException("task", "task is null.");
if (task.Status == TaskStatus.Created)
task.Start();
}
}
CustomDatePickerPage.xaml
<StackLayout Padding="10,60,10,0">
<Label Text="Please enter a date."
FontSize="12" />
<custom:CustomDatePicker MinimumDate="01/01/2022"
MaximumDate="12/31/2022"
Date="06/21/2022"
ImageSource="calendar"
ImageAlignment="Right"
ImageHeight="40"
ImageWidth="40"
ImagePadding="10"
FontSize="Large">
<custom:CustomDatePicker.Behaviors>
<local:CustomDatePickerBehavior />
</custom:CustomDatePicker.Behaviors>
</custom:CustomDatePicker>
</StackLayout>
Behaviors/CustomDatePickerBehavior.cs
public class CustomDatePickerBehavior : Behavior<CustomDatePicker>
{
protected override void OnAttachedTo(CustomDatePicker picker)
{
picker.ImageTapped += Picker_ImageTapped;
base.OnAttachedTo(picker);
}
protected override void OnDetachingFrom(CustomDatePicker picker)
{
picker.ImageTapped -= Picker_ImageTapped;
base.OnDetachingFrom(picker);
}
private void Picker_ImageTapped(object sender, EventArgs e)
{
Action showPicker = (Action)sender;
showPicker.Invoke();
}
}

Are we able to Convert Xamain.iOS custom control to Xamarin.Forms Custom control? If yes then how?

I have requirement to show badge notification icon on NavigationBar right side
for which I have used below code in my native Xamari.iOS project but now I wanted it to use in Xamarin.Forms as Custom Control.
Could someone help me to create Renderer for iOS and Android this type of Custom Controls in Xamarin.Forms?
using System;
using UIKit;
using CoreGraphics;
using CoreAnimation;
using Foundation;
namespace App1.iOS
{
public class BadgeBarButtonItem : UIBarButtonItem
{
#region Fields
UILabel _badge;
string _badgeValue;
UIColor _badgeBGColor;
UIColor _badgeTextColor;
UIColor _badgeBorderColor;
UIFont _badgeFont;
nfloat _badgePadding;
nfloat _badgeMinSize;
nfloat _badgeOriginX;
nfloat _badgeOriginY;
nfloat _badgeBorderWidth;
bool _shouldHideBadgeAtZero;
bool _shouldAnimateBadge;
#endregion
#region Public properties
// Each time you change one of the properties, the badge will refresh with your changes
/// <summary>
/// Gets or sets the badge value to be displayed.
/// </summary>
/// <value>The badge value.</value>
public string BadgeValue
{
get
{
return _badgeValue;
}
set
{
_badgeValue = value;
SetBadgeValue(value);
}
}
/// <summary>
/// Gets or sets the badge background color.
/// </summary>
/// <value>The color of the badge background.</value>
public UIColor BadgeBGColor
{
get
{
return _badgeBGColor;
}
set
{
if (value == null)
throw new ArgumentNullException(nameof(BadgeBGColor));
_badgeBGColor = value;
if (_badge != null)
{
this.RefreshBadge();
}
}
}
/// <summary>
/// Gets or sets the badge text color.
/// </summary>
/// <value>The color of the badge text.</value>
public UIColor BadgeTextColor
{
get
{
return _badgeTextColor;
}
set
{
if (value == null)
throw new ArgumentNullException(nameof(BadgeTextColor));
_badgeTextColor = value;
if (_badge != null)
{
this.RefreshBadge();
}
}
}
/// <summary>
/// Gets or sets the badge border color.
/// </summary>
/// <value>The color of the badge border.</value>
public UIColor BadgeBorderColor
{
get
{
return _badgeBorderColor;
}
set
{
if (value == null)
throw new ArgumentNullException(nameof(BadgeBorderColor));
_badgeBorderColor = value;
if (_badge != null)
{
this.RefreshBadge();
}
}
}
/// <summary>
/// Gets or sets the badge font.
/// </summary>
/// <value>The badge font.</value>
public UIFont BadgeFont
{
get
{
return _badgeFont;
}
set
{
if (value == null)
throw new ArgumentNullException(nameof(BadgeFont));
_badgeFont = value;
if (_badge != null)
{
this.RefreshBadge();
}
}
}
/// <summary>
/// Gets or sets the padding value for the badge.
/// </summary>
/// <value>The badge padding.</value>
public nfloat BadgePadding
{
get
{
return _badgePadding;
}
set
{
_badgePadding = value;
if (_badge != null)
{
this.UpdateBadgeFrame();
}
}
}
/// <summary>
/// Gets or sets the minimum size of the badge minimum.
/// </summary>
/// <value>The minimum size of the badge.</value>
public nfloat BadgeMinSize
{
get
{
return _badgeMinSize;
}
set
{
_badgeMinSize = value;
if (_badge != null)
{
this.UpdateBadgeFrame();
}
}
}
/// <summary>
/// Gets or sets the X value for offsetting the badge over the BarButtonItem.
/// </summary>
/// <value>The badge X origin.</value>
public nfloat BadgeOriginX
{
get
{
return _badgeOriginX;
}
set
{
_badgeOriginX = value;
if (_badge != null)
{
this.UpdateBadgeFrame();
}
}
}
/// <summary>
/// Gets or sets the Y value for offsetting the badge over the BarButtonItem.
/// </summary>
/// <value>The badge Y origin.</value>
public nfloat BadgeOriginY
{
get
{
return _badgeOriginY;
}
set
{
_badgeOriginY = value;
if (_badge != null)
{
this.UpdateBadgeFrame();
}
}
}
/// <summary>
/// Gets or sets the width of the badge border.
/// </summary>
/// <value>The badge border width.</value>
public nfloat BadgeBorderWidth
{
get
{
return _badgeBorderWidth;
}
set
{
_badgeBorderWidth = value;
if (_badge != null)
{
this.RefreshBadge();
}
}
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Utility.BadgeBarButtonItem"/> should remove the badge when reaching zero.
/// </summary>
/// <value><c>true</c> if should hide badge at zero; otherwise, <c>false</c>.</value>
public bool ShouldHideBadgeAtZero
{
get
{
return _shouldHideBadgeAtZero;
}
set
{
_shouldHideBadgeAtZero = value;
if (_badge != null)
{
this.UpdateBadgeFrame();
}
}
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Utility.BadgeBarButtonItem"/> should animate the badge when its value changes.
/// </summary>
/// <value><c>true</c> if should animate badge; otherwise, <c>false</c>.</value>
public bool ShouldAnimateBadge
{
get
{
return _shouldAnimateBadge;
}
set
{
_shouldAnimateBadge = value;
if (_badge != null)
{
this.UpdateBadgeFrame();
}
}
}
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="Utility.BadgeBarButtonItem"/> class.
/// </summary>
/// <param name="customButton">Custom button.</param>
public BadgeBarButtonItem(UIButton customButton)
{
this.CustomView = customButton;
if (this.CustomView != null)
{
Initializer();
}
}
protected virtual void Initializer()
{
// Default design initialization
this.BadgeBGColor = UIColor.Red;
this.BadgeBorderColor = UIColor.White;
this.BadgeTextColor = UIColor.White;
this.BadgeFont = UIFont.SystemFontOfSize(12);
this.BadgePadding = 6;
this.BadgeMinSize = 8;
this.BadgeOriginX = 7;
this.BadgeOriginY = -9;
this.BadgeBorderWidth = 0;
this.ShouldHideBadgeAtZero = true;
this.ShouldAnimateBadge = true;
// Avoids badge to be clipped when animating its scale
this.CustomView.ClipsToBounds = false;
}
#region Utility methods
// Handles badge display when its properties have been changed (background, color and font)
void RefreshBadge()
{
// Change new attributes
_badge.TextColor = this.BadgeTextColor;
_badge.BackgroundColor = this.BadgeBGColor;
_badge.Font = this.BadgeFont;
SetBorder();
}
void UpdateBadgeFrame()
{
// When the value changes the badge could need to get bigger
// Calculate expected size to fit new value
// Use an intermediate label to get expected size thanks to sizeToFit
// We don't call sizeToFit on the true label to avoid bad display
UILabel frameLabel = DuplicateLabel(_badge);
frameLabel.SizeToFit();
CGSize expectedLabelSize = frameLabel.Frame.Size;
// Make sure that for small value, the badge will be big enough
nfloat minHeight = expectedLabelSize.Height;
// Using a const we make sure the badge respect the minimum size
minHeight = (minHeight < this.BadgeMinSize) ? this.BadgeMinSize : expectedLabelSize.Height;
nfloat minWidth = expectedLabelSize.Width;
nfloat padding = this.BadgePadding;
// Using const we make sure the badge doesn't get too smal
minWidth = (minWidth < minHeight) ? minHeight : expectedLabelSize.Width;
_badge.Frame = new CGRect(this.BadgeOriginX, this.BadgeOriginY, minWidth + padding, minHeight + padding);
_badge.Layer.CornerRadius = (minHeight + padding) / 2;
_badge.Layer.MasksToBounds = true;
}
// Handle the badge changing value
void UpdateBadgeValueAnimated(bool animated)
{
// Bounce animation on badge if value changed and if animation authorized
if (animated && this.ShouldAnimateBadge && _badge.Text != this.BadgeValue)
{
var animation = new CABasicAnimation();
animation.KeyPath = #"transform.scale";
animation.From = NSObject.FromObject(1.5);
animation.To = NSObject.FromObject(1);
animation.Duration = 0.2;
animation.TimingFunction = new CAMediaTimingFunction(0.4f, 1.3f, 1f, 1f);
_badge.Layer.AddAnimation(animation, #"bounceAnimation");
}
// Set the new value
_badge.Text = this.BadgeValue;
// Animate the size modification if needed
double duration = animated ? 0.2 : 0;
UIView.Animate(duration, UpdateBadgeFrame);
}
static UILabel DuplicateLabel(UILabel labelToCopy)
{
var duplicateLabel = new UILabel(labelToCopy.Frame);
duplicateLabel.Text = labelToCopy.Text;
duplicateLabel.Font = labelToCopy.Font;
return duplicateLabel;
}
void RemoveBadge()
{
if (_badge != null)
{
// Animate badge removal
UIView.AnimateNotify(0.15f, 0.0F,
UIViewAnimationOptions.CurveEaseIn,
() => {
_badge.Transform = CGAffineTransform.MakeScale(0.1f, 0.1f);
},
completed => {
_badge.RemoveFromSuperview();
_badge = null;
}
);
}
}
#endregion
#region Setter
void SetBadgeValue(string badgeValue)
{
// Set new value
_badgeValue = badgeValue;
// When changing the badge value check if we need to remove the badge
if (string.IsNullOrEmpty(badgeValue) || (badgeValue == #"0" && this.ShouldHideBadgeAtZero))
{
RemoveBadge();
}
else if (_badge == null)
{
// Create a new badge because it doesn't exist
_badge = new UILabel(new CGRect(this.BadgeOriginX, this.BadgeOriginY, 20, 20))
{
TextColor = this.BadgeTextColor,
BackgroundColor = this.BadgeBGColor,
Font = this.BadgeFont,
TextAlignment = UITextAlignment.Center,
};
// Fix for iOS 9: Correctly apply the CornerRadius later on
_badge.Layer.MasksToBounds = true;
SetBorder();
this.CustomView.AddSubview(_badge);
this.UpdateBadgeValueAnimated(false);
}
else
{
this.UpdateBadgeValueAnimated(true);
}
}
void SetBorder()
{
if (this.BadgeBorderWidth > 0)
{
_badge.Layer.BorderColor = BadgeBorderColor.CGColor;
}
_badge.Layer.BorderWidth = this.BadgeBorderWidth;
}
#endregion
}
}
I got this control from below link please refer it:-
https://github.com/fabiogaragiola/BadgeBarButtonItem
You should create custom renderer for page, then in OnElementChange you can call this.NavigationItem.RightBarButtonItem = barButton; to add your custom toolbar button.

object reference is required, also dump object into messagebox tips

i'm getting an error with this code new RoutedEventHandler(Notifications.showNotifications(window)); saying:
An object reference is required for the non-static field, method, or
property 'Notifications.showNotifications(Window)'
Also, can I get an example of how I could dump out each Dictionary object from within my _notifications object in the runtimeObject class? I need to do this when showNotifications is called!
When I say dump out, based on the test notifications which I try to add in the code, I just want to display a MessageBox to show that they are being added, so using the code above the MessageBox would show:
MessageBox
Error Code: 1, Text: Error 1
Error Code: 2, Text: Normal Message
Error Code: 3, Text: Tip
App.xaml.cs
namespace Test_Project
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//Startup
Window main = new MainWindow();
main.Show();
//Attach Event Handlers to MainWindow
if (!attachEventHandlers(main))
{
MessageBox.Show("Fatal Error: Unable to attach event handlers, contact administrator!",
"Fatal Error!",
MessageBoxButton.OK,
MessageBoxImage.Exclamation);
}
}
public bool attachEventHandlers(Window window)
{
//window.MouseMove += new RoutedEventHandler(Notifications.showNotifications(window));
return true;
}
}
public class Notifications
{
/// <summary>
/// Show the notifications
/// </summary>
/// <param name="window"></param>
public void showNotifications(Window window)
{
// Find the resource, then cast it to a runtimeObject
var runtime = (runtimeObject)Application.Current.TryFindResource("runtimeVariables");
//Create messagebox with all the notifications in to test
}
public bool addNotifications()
{
// Find the resource, then cast it to a runtimeObject
var runtime = (runtimeObject)Application.Current.TryFindResource("runtimeVariables");
//Create Dictionary
Dictionary<int, string> arr = new Dictionary<int, string>();
arr.Add(1, "Error 1");
arr.Add(2, "Normal Message");
arr.Add(3, "Tip");
//Create test notifications
runtime.notifications.Add(arr);
return true;
}
}
/// <summary>
/// Global values for use during application runtime
/// </summary>
public class runtimeObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
//Can the application be closed?
private bool _inProgress = false;
public bool inProgress
{
get { return _inProgress; }
set
{
if (_inProgress != value)
{
_inProgress = value;
OnPropertyChanged("inProgress");
}
}
}
/// <summary>
/// Notifications held
/// Array((int)type,(string)"message")
/// </summary>
private List<Dictionary<int, string>> _notifications;
public List<Dictionary<int, string>> notifications
{
get { return _notifications; }
set
{
if (_notifications != value)
{
_notifications = value;
OnPropertyChanged("notifications");
}
}
}
//Selected folder to search in
private int _uploadProgress = 0;
public int uploadProgress
{
get { return _uploadProgress; }
set
{
if (_uploadProgress != value)
{
//int Angle = (_uploadProgress * 360) / 100;
//Classes.CircularProgress.RenderArc(Angle);
_uploadProgress = value;
OnPropertyChanged("uploadProgress");
}
}
}
}
}

DependencyProperty not notifying UI on NotifyCollectionChanged event

I am working with a custom control which has a selected items dependency property which I have wired up the collection changed event but the UI is not notified and the PropertyChanged event is always null. Normally I would say this is a datacontext issue. but I cannot change the data context on the control as no data will display.
public ObservableCollection<object> SelectedItems
{
get { return (ObservableCollection<object>)GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedItems. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.RegisterAttached("SelectedItems", typeof(ObservableCollection<object>),
typeof(MultiSelectComboBox),
new System.Windows.PropertyMetadata(new ObservableCollection<object>(), new PropertyChangedCallback(SelectedItemsPropertyChanged)));
private static void SelectedItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MultiSelectComboBox relay = d as MultiSelectComboBox;
if (e.OldValue != null)
{
var coll = (INotifyCollectionChanged)e.OldValue;
coll.CollectionChanged -= relay.SelectedItemsCollectionChanged;
}
if (e.NewValue != null)
{
var coll = (INotifyCollectionChanged)e.NewValue;
coll.CollectionChanged += relay.SelectedItemsCollectionChanged;
}
}
Above is the property declaration it is bound in xaml to an ObservableCollection on the ViewModel. What am I missing. The control implements INotifyPropertyChanged.
I have added more code below. This is the original code and I would like to change the same property to a dependency property for binding to collection puposes.
namespace Kepler.SilverlightControls.MultiSelectComboBox
{
/// <summary>
/// MultiSelect ComboBox
/// </summary>
public class MultiSelectComboBox : Telerik.Windows.Controls.RadComboBox, INotifyPropertyChanged
{
#region Events
/// <summary>
/// Est appelé quand une propriété change
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of MultiSelectComboBox
/// </summary>
public MultiSelectComboBox()
{
ClearSelectionButtonVisibility = Visibility.Collapsed;
string xaml = #"<DataTemplate
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:local=""clr-namespace:Kepler.SilverlightControls.MultiSelectComboBox;assembly=Kepler.SilverlightControls"">
<TextBlock TextWrapping=""Wrap"" local:MultiSelectComboBoxService.SelectionBoxLoaded=""True"" />
</DataTemplate>";
var selectionBoxTemplate = (DataTemplate)XamlReader.Load(xaml);
SelectionBoxTemplate = selectionBoxTemplate;
EmptySelectionBoxTemplate = selectionBoxTemplate;
xaml = #"<DataTemplate
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:local=""clr-namespace:Kepler.SilverlightControls.MultiSelectComboBox;assembly=Kepler.SilverlightControls"">
<CheckBox local:MultiSelectComboBoxService.ComboBoxItemLoaded=""True""
IsChecked=""{Binding Path=(local:MultiSelectComboBoxService.IsChecked), Mode=TwoWay, RelativeSource={RelativeSource Self}}"" />
</DataTemplate>";
ItemTemplate = (DataTemplate)XamlReader.Load(xaml);
}
#endregion
#region Propriétés
/// <summary>
/// IsCheckedBindingPath Property
/// </summary>
public string IsCheckedBindingPath
{
get { return (string)GetValue(IsCheckedBindingPathProperty); }
set { SetValue(IsCheckedBindingPathProperty, value); }
}
// Using a DependencyProperty as the backing store for IsCheckedBindingPath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckedBindingPathProperty =
DependencyProperty.Register("IsCheckedBindingPath", typeof(string), typeof(MultiSelectComboBox), new PropertyMetadata(null, (obj, e) =>
{
Telerik.Windows.Controls.TextSearch.SetTextPath(obj, e.NewValue as string);
}));
/// <summary>
/// DisplayBindingPath Property
/// </summary>
public static readonly DependencyProperty DisplayBindingPathProperty = DependencyProperty.Register(
"DisplayBindingPath", typeof(string), typeof(MultiSelectComboBox), new PropertyMetadata(null, (obj, e) =>
{
Telerik.Windows.Controls.TextSearch.SetTextPath(obj, e.NewValue as string);
}));
/// <summary>
/// Gets or sets the display member path (we can't reuse DisplayMemberPath property)
/// </summary>
public string DisplayBindingPath
{
get { return GetValue(DisplayBindingPathProperty) as string; }
set { SetValue(DisplayBindingPathProperty, value); }
}
private ObservableCollection<object> _selectedItems;
/// <summary>
/// Gets the selected items
/// </summary>
public ObservableCollection<object> SelectedItems
{
get
{
if (_selectedItems == null)
{
_selectedItems = new ObservableCollection<object>();
_selectedItems.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectedItemsCollectionChanged);
}
return _selectedItems;
}
}
private ObservableCollection<object> _selectedValues;
/// <summary>
/// Gets the selected values
/// </summary>
public ObservableCollection<object> SelectedValues
{
get
{
if (_selectedValues == null)
{
_selectedValues = new ObservableCollection<object>();
_selectedValues.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectedValuesCollectionChanged);
}
return _selectedValues;
}
}
#endregion
#region Methods
/// <summary>
/// Called when the Items property changed
/// </summary>
/// <param name="e">change informations</param>
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
int idx;
var selectedItems = SelectedItems;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Reset:
var items = e.NewItems;
if (items == null)
{
var selected = new List<object>();
foreach (var item in this.ItemsSource)
{
PropertyInfo isCheckedBindingPathProperty = this.IsCheckedBindingPath != null ? item.GetType().GetProperty(this.IsCheckedBindingPath) : null;
if (isCheckedBindingPathProperty != null
&& (bool)isCheckedBindingPathProperty.GetValue(item,null) == true)
{
selected.Add(item);
SelectedValues.Add(item.GetType().GetProperty(SelectedValuePath).GetValue(item, null));
}
}
items = selected;
}
if (items != null)
{
foreach (object value in SelectedValues)
{
foreach (object item in items)
{
if (GetSelectedValue(item).Equals(value) && !selectedItems.Contains(item))
{
selectedItems.Add(item);
}
}
}
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (object item in e.OldItems)
{
idx = selectedItems.IndexOf(item);
if (idx >= 0)
{
selectedItems.RemoveAt(idx);
}
}
break;
}
}
private void RemoveCollectionChangedEvents()
{
SelectedItems.CollectionChanged -= new NotifyCollectionChangedEventHandler(SelectedItemsCollectionChanged);
SelectedValues.CollectionChanged -= new NotifyCollectionChangedEventHandler(SelectedValuesCollectionChanged);
}
private void AddCollectionChangedEvents()
{
SelectedItems.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectedItemsCollectionChanged);
SelectedValues.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectedValuesCollectionChanged);
}
private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (SelectedValuePath != null)
{
RemoveCollectionChangedEvents();
try
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
AddSelectedValues(e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
RemoveSelectedValues(e.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
RemoveSelectedValues(e.OldItems);
AddSelectedValues(e.NewItems);
break;
case NotifyCollectionChangedAction.Reset:
SelectedValues.Clear();
foreach (object item in Items)
{
UpdateSelectedItem(item, false);
}
AddSelectedValues(e.NewItems);
break;
}
}
finally
{
AddCollectionChangedEvents();
}
}
RaiseSelectedItemsPropertyChanged();
}
private void RemoveSelectedValues(IList items)
{
foreach (var item in items)
{
SelectedValues.Remove(GetSelectedValue(item));
UpdateSelectedItem(item, false);
}
}
private void AddSelectedValues(IList items)
{
if (items != null)
{
object selectedValue;
foreach (var item in items)
{
selectedValue = GetSelectedValue(item);
if (!SelectedValues.Contains(selectedValue))
{
SelectedValues.Add(selectedValue);
}
UpdateSelectedItem(item, true);
}
}
}
private object GetSelectedValue(object item)
{
return DataControlHelper.GetPropertyInfo(item.GetType(), SelectedValuePath).GetValue(item, null);
}
private void SelectedValuesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RemoveCollectionChangedEvents();
try
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
AddSelectedItems(e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
RemoveSelectedItems(e.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
RemoveSelectedItems(e.OldItems);
AddSelectedItems(e.NewItems);
break;
case NotifyCollectionChangedAction.Reset:
var selectedItems = SelectedItems.ToList();
SelectedItems.Clear();
foreach (object item in selectedItems)
{
UpdateSelectedItem(item, false);
}
AddSelectedItems(e.NewItems);
break;
}
}
finally
{
AddCollectionChangedEvents();
}
RaiseSelectedItemsPropertyChanged();
}
private void RaiseSelectedItemsPropertyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedItems"));
// To update the selection box
}
}
private void RemoveSelectedItems(IList values)
{
object item;
foreach (var value in values)
{
item = SelectedItems.FirstOrDefault(e => GetSelectedValue(e).Equals(value));
if (item != null)
{
SelectedItems.Remove(item);
UpdateSelectedItem(item, false);
}
}
}
private void AddSelectedItems(IList values)
{
if (values != null)
{
object item;
foreach (var value in values)
{
item = Items.FirstOrDefault(e => GetSelectedValue(e).Equals(value));
if (item != null)
{
SelectedItems.Add(item);
UpdateSelectedItem(item, true);
}
}
}
}
private void UpdateSelectedItem(object item, bool select)
{
var obj = ItemContainerGenerator.ContainerFromItem(item);
if (obj != null)
{
var cb = obj.FindChildByType<CheckBox>();
if (cb != null && cb.IsChecked != select)
{
cb.IsChecked = select;
}
}
}
/// <summary>
/// Create a new ComboBox item
/// </summary>
/// <returns>a new ComboBox item</returns>
protected override DependencyObject GetContainerForItemOverride()
{
return new MultiSelectComboBoxItem(this);
}
protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e)
{
base.OnKeyDown(e);
IsDropDownOpen = true;
}
#endregion
}
}
This service class is below:
namespace Kepler.SilverlightControls.MultiSelectComboBox
{
/// <summary>
/// Service for the MultiSelect comboBox
/// </summary>
public static class MultiSelectComboBoxService
{
/// <summary>
/// IsChecked property
/// </summary>
public static DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached("IsChecked",
typeof(bool), typeof(MultiSelectComboBoxService), new PropertyMetadata(false, (obj, e) =>
{
MultiSelectComboBoxItem comboBoxItem = obj.GetVisualParent<MultiSelectComboBoxItem>();
if (comboBoxItem != null)
{
MultiSelectComboBox comboBox = comboBoxItem.ParentComboBox;
var selectedItems = (IList)comboBox.SelectedItems;
object item = comboBoxItem.DataContext;
PropertyInfo isCheckedBindingPathProperty = item.GetType().GetProperty(comboBox.IsCheckedBindingPath);
isCheckedBindingPathProperty.SetValue(item, e.NewValue,null);
if ((bool)e.NewValue)
{
if (!selectedItems.Contains(item))
{
selectedItems.Add(item);
}
}
else
{
selectedItems.Remove(item);
}
}
}));
/// <summary>
/// Gets a value indicating if the object is checked or not
/// </summary>
/// <param name="obj">DependencyObject</param>
/// <returns>a value indicating if the object is checked or not</returns>
public static bool GetIsChecked(DependencyObject obj)
{
return (bool)obj.GetValue(IsCheckedProperty);
}
/// <summary>
/// Sets a value indicating if the object is checked or not
/// </summary>
/// <param name="obj">DependencyObject</param>
/// <param name="value">the value indicating if the object is checked or not</param>
public static void SetIsChecked(DependencyObject obj, bool value)
{
obj.SetValue(IsCheckedProperty, value);
}
/// <summary>
/// SelectionBoxLoaded property called on SelectionBox load
/// </summary>
public static DependencyProperty SelectionBoxLoadedProperty = DependencyProperty.RegisterAttached("SelectionBoxLoaded",
typeof(bool), typeof(MultiSelectComboBoxService), new PropertyMetadata(false, (obj, e) =>
{
TextBlock targetElement = obj as TextBlock;
if (targetElement != null)
{
targetElement.Loaded += new RoutedEventHandler(targetElement_Loaded);
}
}));
private static void targetElement_Loaded(object sender, RoutedEventArgs e)
{
TextBlock targetElement = (TextBlock)sender;
targetElement.Loaded -= new RoutedEventHandler(targetElement_Loaded);
MultiSelectComboBox comboBox = targetElement.GetVisualParent<MultiSelectComboBox>();
if (comboBox != null)
{
targetElement.SetBinding(TextBlock.TextProperty, new Binding("SelectedItems")
{
Converter = new MultiSelectComboxConverter(),
Source = comboBox,
ConverterParameter = comboBox.DisplayBindingPath
});
}
}
/// <summary>
/// Gets the value indicating if the object is loaded or not
/// </summary>
/// <param name="obj">DependencyObject</param>
/// <returns>the value indicating if the object is loaded or not</returns>
public static bool GetSelectionBoxLoaded(DependencyObject obj)
{
return (bool)obj.GetValue(SelectionBoxLoadedProperty);
}
/// <summary>
/// Sets the value indicating if the object is loaded or not
/// </summary>
/// <param name="obj">DependencyObject</param>
/// <param name="value">the value indicating if the object is loaded or not</param>
public static void SetSelectionBoxLoaded(DependencyObject obj, bool value)
{
obj.SetValue(SelectionBoxLoadedProperty, value);
}
/// <summary>
/// ComboBoxItemLoaded called on ComboBoxItem load
/// </summary>
public static DependencyProperty ComboBoxItemLoadedProperty = DependencyProperty.RegisterAttached("ComboBoxItemLoaded",
typeof(bool), typeof(MultiSelectComboBoxService), new PropertyMetadata(false, (obj, e) =>
{
CheckBox targetElement = obj as CheckBox;
if (targetElement != null)
{
targetElement.Loaded += new RoutedEventHandler(comboBoxItem_Loaded);
targetElement.SetBinding(MultiSelectComboBoxService.DataContextProperty, new Binding());
}
}));
private static void comboBoxItem_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
MultiSelectComboBox comboBox = GetComboBox(element);
if (comboBox != null)
{
element.SetBinding(CheckBox.ContentProperty, new Binding(comboBox.DisplayBindingPath));
//Binding binding = new Binding(comboBox.IsCheckedBindingPath);
//binding.Mode = BindingMode.TwoWay;
//element.SetBinding(CheckBox.IsCheckedProperty, binding);
}
}
/// <summary>
///Gets the value indicating if the item is loaded or not
/// </summary>
/// <param name="obj">DependencyObject</param>
/// <returns>the value indicating if the item is loaded or not</returns>
public static bool GetComboBoxItemLoaded(DependencyObject obj)
{
return (bool)obj.GetValue(ComboBoxItemLoadedProperty);
}
/// <summary>
/// Sets the value indicating if the item is loaded or not
/// </summary>
/// <param name="obj">DependencyObject</param>
/// <param name="value">the value indicating if the item is loaded or not</param>
public static void SetComboBoxItemLoaded(DependencyObject obj, bool value)
{
obj.SetValue(ComboBoxItemLoadedProperty, value);
}
private static MultiSelectComboBox GetComboBox(DependencyObject targetElement)
{
MultiSelectComboBoxItem item = targetElement.GetVisualParent<MultiSelectComboBoxItem>();
if (item != null)
{
return item.ParentComboBox;
}
return null;
}
private static DependencyProperty DataContextProperty = DependencyProperty.RegisterAttached("DataContext",
typeof(object), typeof(MultiSelectComboBoxService), new PropertyMetadata(null, (obj, e) =>
{
CheckBox checkBox = (CheckBox)obj;
MultiSelectComboBox comboBox = GetComboBox(checkBox);
if (comboBox != null)
{
checkBox.IsChecked = comboBox.SelectedItems.Contains(checkBox.DataContext);
}
}));
private static object GetDataContext(DependencyObject obj)
{
return obj.GetValue(DataContextProperty);
}
private static void SetDataContext(DependencyObject obj, object value)
{
obj.SetValue(DataContextProperty, value);
}
}
}
The converter is as follows:
namespace Kepler.SilverlightControls.MultiSelectComboBox
{
#region Méthodes
public class MultiSelectComboxConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string displayMemberPath = parameter as string;
if (String.IsNullOrWhiteSpace(displayMemberPath))
{
return String.Empty;
}
PropertyInfo propertyInfo;
return string.Join(", ", (value as IEnumerable<object>).Select(item =>
{
propertyInfo = DataControlHelper.GetPropertyInfo(item.GetType(), displayMemberPath);
if (propertyInfo == null)
{
return String.Empty;
}
return propertyInfo.GetValue(item, null);
}).ToArray());
}
/// <summary>
/// Not implemented
/// </summary>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
You must not register the dependency property as an attached property, but as a regular dependency property instead. Moreover, you should not use new ObservableCollection<object>() as default property value, as that would use the same collection instance as default value for the property on all instances of your MultiSelectComboBox.
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register( // Register instead of RegisterAttached
"SelectedItems",
typeof(ObservableCollection<object>),
typeof(MultiSelectComboBox),
new PropertyMetadata(SelectedItemsPropertyChanged)); // no default value
I'd also recommend not to use ObservableCollection<object> as the property type, but simply ICollection or IEnumerable instead. This would allow for other implementations of INotifyCollectionChanged in the concrete collection type.
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register(
"SelectedItems",
typeof(ICollection),
typeof(MultiSelectComboBox),
new PropertyMetadata(SelectedItemsPropertyChanged));
public ICollection SelectedItems
{
get { return (ICollection)GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
private static void SelectedItemsPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var comboBox = (MultiSelectComboBox)obj;
var oldCollection = e.OldValue as INotifyCollectionChanged;
var newCollection = e.NewValue as INotifyCollectionChanged;
if (oldCollection != null)
{
oldCollection.CollectionChanged -= SelectedItemsCollectionChanged;
}
if (newCollection != null)
{
newCollection.CollectionChanged += SelectedItemsCollectionChanged;
}
}
private static void SelectedItemsCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
...
}
}
Please also note that the MultiSelectComboBox does not need to implement INotifyPropertyChanged. This would only be necessary for notifying changes of properties that are no dependency properties.

how to change a button into a imagebutton in asp.net c#

alt text http://[url=http://www.freeimagehosting.net/][img]http://www.freeimagehosting.net/uploads/06e679a07d.jpg[/img][/url]
How to change the button into image button... the button in the beginning has "Pick a date" when clicked a calender pops out and the when a date is selected a label at the bottom reading the date comes in and the text on the button changes to disabled... i want to palce a imagebutton having a image icon of the calender and rest of the function will be the same....
the code as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
[assembly: TagPrefix("DatePicker", "EWS")]
namespace EclipseWebSolutions.DatePicker
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:DatePicker runat=server>")]
[DefaultEvent("SelectionChanged")]
[ValidationProperty("TextValue")]
public class DatePicker : WebControl, INamingContainer
{
#region Properties
public TextBox txtDate = new TextBox();
public Calendar calDate = new Calendar();
public Button btnDate = new Button();
public Panel pnlCalendar = new Panel();
private enum ViewStateConstants
{
ValidationGroup,
RegularExpression,
ErrorMessage,
RegExText,
CalendarPosition,
FormatString,
ExpandLabel,
CollapseLabel,
ApplyDefaultStyle,
CausesValidation,
}
/// <summary>
/// Defines the available display modes of this calendar.
/// </summary>
public enum CalendarDisplay
{
DisplayRight,
DisplayBelow
}
/// <summary>
/// Where to display the popup calendar.
/// </summary>
[Category("Behaviour")]
[Localizable(true)]
public CalendarDisplay CalendarPosition
{
get
{
if (ViewState[ViewStateConstants.CalendarPosition.ToString()] == null)
{
ViewState[ViewStateConstants.CalendarPosition.ToString()] = CalendarDisplay.DisplayRight;
}
return (CalendarDisplay)ViewState[ViewStateConstants.CalendarPosition.ToString()];
}
set
{
ViewState[ViewStateConstants.CalendarPosition.ToString()] = value;
}
}
/// <summary>
/// Text version of the control's value, for use by ASP.NET validators.
/// </summary>
public string TextValue
{
get { return txtDate.Text; }
}
/// <summary>
/// Holds the current date value of this control.
/// </summary>
[Category("Behaviour")]
[Localizable(true)]
[Bindable(true, BindingDirection.TwoWay)]
public DateTime DateValue
{
get
{
try
{
if (txtDate.Text == "") return DateTime.MinValue;
DateTime val = DateTime.Parse(txtDate.Text);
return val;
}
catch (ArgumentNullException)
{
return DateTime.MinValue;
}
catch (FormatException)
{
return DateTime.MinValue;
}
}
set
{
if (value == DateTime.MinValue)
{
txtDate.Text = "";
}
else
{
txtDate.Text = value.ToShortDateString();
}
}
}
[Category("Behavior"), Themeable(false), DefaultValue("")]
public virtual string ValidationGroup
{
get
{
if (ViewState[ViewStateConstants.ValidationGroup.ToString()] == null)
{
return string.Empty;
}
else
{
return (string)ViewState[ViewStateConstants.ValidationGroup.ToString()];
}
}
set
{
ViewState[ViewStateConstants.ValidationGroup.ToString()] = value;
}
}
/// <summary>
/// The label of the exand button. Shown when the calendar is hidden.
/// </summary>
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("PickDate")]
[Localizable(true)]
public string ExpandButtonLabel
{
get
{
String s = (String)ViewState[ViewStateConstants.ExpandLabel.ToString()];
return ((s == null) ? "PickDate" : s);
}
set
{
ViewState[ViewStateConstants.ExpandLabel.ToString()] = value;
}
}
/// <summary>
/// The label of the collapse button. Shown when the calendar is visible.
/// </summary>
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("Disabled")]
[Localizable(true)]
public string CollapseButtonLabel
{
get
{
String s = (String)ViewState[ViewStateConstants.CollapseLabel.ToString()];
return ((s == null) ? "Disabled" : s);
}
set
{
ViewState[ViewStateConstants.CollapseLabel.ToString()] = value;
}
}
/// <summary>
/// Whether to apply the default style. Disable this if you want to apply a custom style, or to use themes and skins
/// to style the control.
/// </summary>
[Category("Appearance")]
[DefaultValue(true)]
[Localizable(true)]
public bool ApplyDefaultStyle
{
get
{
if (ViewState[ViewStateConstants.ApplyDefaultStyle.ToString()] == null)
{
ViewState[ViewStateConstants.ApplyDefaultStyle.ToString()] = true;
}
return (bool)ViewState[ViewStateConstants.ApplyDefaultStyle.ToString()];
}
set
{
ViewState[ViewStateConstants.ApplyDefaultStyle.ToString()] = value;
}
}
/// <summary>
/// Causes Validation
/// </summary>
[Category("Appearance")]
[DefaultValue(false)]
[Localizable(false)]
public bool CausesValidation
{
get
{
if (ViewState[ViewStateConstants.CausesValidation.ToString()] == null)
{
ViewState[ViewStateConstants.CausesValidation.ToString()] = false;
}
return (bool)ViewState[ViewStateConstants.CausesValidation.ToString()];
}
set
{
ViewState[ViewStateConstants.CausesValidation.ToString()] = value;
btnDate.CausesValidation = value;
}
}
#endregion
#region Events
/// <summary>
/// A day was selected from the calendar control.
/// </summary>
public event EventHandler SelectionChanged;
protected virtual void OnSelectionChanged()
{
if (SelectionChanged != null) // only raise the event if someone is listening.
{
SelectionChanged(this, EventArgs.Empty);
}
}
#endregion
#region Event Handlers
/// <summary>
/// The +/- button was clicked.
/// </summary>
protected void btnDate_Click(object sender, System.EventArgs e)
{
if (!calDate.Visible)
{
// expand the calendar
calDate.Visible = true;
txtDate.Enabled = false;
btnDate.Text = CollapseButtonLabel;
if (DateValue != DateTime.MinValue)
{
calDate.SelectedDate = DateValue;
calDate.VisibleDate = DateValue;
}
}
else
{
// collapse the calendar
calDate.Visible = false;
txtDate.Enabled = true;
btnDate.Text = ExpandButtonLabel;
}
}
/// <summary>
/// A date was selected from the calendar.
/// </summary>
protected void calDate_SelectionChanged(object sender, System.EventArgs e)
{
calDate.Visible = false;
txtDate.Visible = true;
btnDate.Text = ExpandButtonLabel;
txtDate.Enabled = true;
txtDate.Text = calDate.SelectedDate.ToShortDateString();
OnSelectionChanged();
}
#endregion
/// <summary>
/// Builds the contents of this control.
/// </summary>
protected override void CreateChildControls()
{
btnDate.Text = ExpandButtonLabel;
btnDate.CausesValidation = CausesValidation;
txtDate.ID = "txtDate";
calDate.Visible = false;
if (ApplyDefaultStyle)
{
calDate.BackColor = System.Drawing.Color.White;
calDate.BorderColor = System.Drawing.Color.FromArgb(10066329);
calDate.CellPadding = 2;
calDate.DayNameFormat = DayNameFormat.Shortest;
calDate.Font.Name = "Verdana";
calDate.Font.Size = FontUnit.Parse("8pt");
calDate.ForeColor = System.Drawing.Color.Black;
calDate.Height = new Unit(150, UnitType.Pixel);
calDate.Width = new Unit(180, UnitType.Pixel);
calDate.DayHeaderStyle.BackColor = System.Drawing.Color.FromArgb(228, 228, 228);
calDate.DayHeaderStyle.Font.Size = FontUnit.Parse("7pt");
calDate.TitleStyle.Font.Bold = true;
calDate.WeekendDayStyle.BackColor = System.Drawing.Color.FromArgb(255, 255, 204);
}
ConnectEventHandlers();
pnlCalendar.Controls.Add(calDate);
pnlCalendar.Style["position"] = "absolute";
pnlCalendar.Style["filter"] = "alpha(opacity=95)";
pnlCalendar.Style["-moz-opacity"] = ".95";
pnlCalendar.Style["opacity"] = ".95";
pnlCalendar.Style["z-index"] = "2";
pnlCalendar.Style["background-color"] = "White";
if (CalendarPosition == CalendarDisplay.DisplayBelow)
{
pnlCalendar.Style["margin-top"] = "27px";
}
else
{
pnlCalendar.Style["display"] = "inline";
}
Controls.Add(txtDate);
Controls.Add(pnlCalendar);
Controls.Add(btnDate);
base.CreateChildControls();
}
/// <summary>
/// Render the contents of this control.
/// </summary>
/// <param name="output">The HtmlTextWriter to use.</param>
protected override void RenderContents(HtmlTextWriter output)
{
switch (CalendarPosition)
{
case CalendarDisplay.DisplayRight:
{
txtDate.RenderControl(output);
btnDate.RenderControl(output);
pnlCalendar.RenderControl(output);
break;
}
case CalendarDisplay.DisplayBelow:
{
pnlCalendar.RenderControl(output);
txtDate.RenderControl(output);
btnDate.RenderControl(output);
break;
}
}
}
/// <summary>
/// Connect event handlers to events.
/// </summary>
private void ConnectEventHandlers()
{
btnDate.Click += new System.EventHandler(btnDate_Click);
calDate.SelectionChanged += new System.EventHandler(calDate_SelectionChanged);
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
[assembly: TagPrefix("DatePicker", "EWS")]
namespace EclipseWebSolutions.DatePicker
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:DatePicker runat=server>")]
[DefaultEvent("SelectionChanged")]
[ValidationProperty("TextValue")]
public class DatePicker : WebControl, INamingContainer
{
#region Properties
public TextBox txtDate = new TextBox();
public Calendar calDate = new Calendar();
public Button btnDate = new Button();
public Panel pnlCalendar = new Panel();
private enum ViewStateConstants
{
ValidationGroup,
RegularExpression,
ErrorMessage,
RegExText,
CalendarPosition,
FormatString,
ExpandLabel,
CollapseLabel,
ApplyDefaultStyle,
CausesValidation,
}
/// <summary>
/// Defines the available display modes of this calendar.
/// </summary>
public enum CalendarDisplay
{
DisplayRight,
DisplayBelow
}
/// <summary>
/// Where to display the popup calendar.
/// </summary>
[Category("Behaviour")]
[Localizable(true)]
public CalendarDisplay CalendarPosition
{
get
{
if (ViewState[ViewStateConstants.CalendarPosition.ToString()] == null)
{
ViewState[ViewStateConstants.CalendarPosition.ToString()] = CalendarDisplay.DisplayRight;
}
return (CalendarDisplay)ViewState[ViewStateConstants.CalendarPosition.ToString()];
}
set
{
ViewState[ViewStateConstants.CalendarPosition.ToString()] = value;
}
}
/// <summary>
/// Text version of the control's value, for use by ASP.NET validators.
/// </summary>
public string TextValue
{
get { return txtDate.Text; }
}
/// <summary>
/// Holds the current date value of this control.
/// </summary>
[Category("Behaviour")]
[Localizable(true)]
[Bindable(true, BindingDirection.TwoWay)]
public DateTime DateValue
{
get
{
try
{
if (txtDate.Text == "") return DateTime.MinValue;
DateTime val = DateTime.Parse(txtDate.Text);
return val;
}
catch (ArgumentNullException)
{
return DateTime.MinValue;
}
catch (FormatException)
{
return DateTime.MinValue;
}
}
set
{
if (value == DateTime.MinValue)
{
txtDate.Text = "";
}
else
{
txtDate.Text = value.ToShortDateString();
}
}
}
[Category("Behavior"), Themeable(false), DefaultValue("")]
public virtual string ValidationGroup
{
get
{
if (ViewState[ViewStateConstants.ValidationGroup.ToString()] == null)
{
return string.Empty;
}
else
{
return (string)ViewState[ViewStateConstants.ValidationGroup.ToString()];
}
}
set
{
ViewState[ViewStateConstants.ValidationGroup.ToString()] = value;
}
}
/// <summary>
/// The label of the exand button. Shown when the calendar is hidden.
/// </summary>
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("PickDate")]
[Localizable(true)]
public string ExpandButtonLabel
{
get
{
String s = (String)ViewState[ViewStateConstants.ExpandLabel.ToString()];
return ((s == null) ? "PickDate" : s);
}
set
{
ViewState[ViewStateConstants.ExpandLabel.ToString()] = value;
}
}
/// <summary>
/// The label of the collapse button. Shown when the calendar is visible.
/// </summary>
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("Disabled")]
[Localizable(true)]
public string CollapseButtonLabel
{
get
{
String s = (String)ViewState[ViewStateConstants.CollapseLabel.ToString()];
return ((s == null) ? "Disabled" : s);
}
set
{
ViewState[ViewStateConstants.CollapseLabel.ToString()] = value;
}
}
/// <summary>
/// Whether to apply the default style. Disable this if you want to apply a custom style, or to use themes and skins
/// to style the control.
/// </summary>
[Category("Appearance")]
[DefaultValue(true)]
[Localizable(true)]
public bool ApplyDefaultStyle
{
get
{
if (ViewState[ViewStateConstants.ApplyDefaultStyle.ToString()] == null)
{
ViewState[ViewStateConstants.ApplyDefaultStyle.ToString()] = true;
}
return (bool)ViewState[ViewStateConstants.ApplyDefaultStyle.ToString()];
}
set
{
ViewState[ViewStateConstants.ApplyDefaultStyle.ToString()] = value;
}
}
/// <summary>
/// Causes Validation
/// </summary>
[Category("Appearance")]
[DefaultValue(false)]
[Localizable(false)]
public bool CausesValidation
{
get
{
if (ViewState[ViewStateConstants.CausesValidation.ToString()] == null)
{
ViewState[ViewStateConstants.CausesValidation.ToString()] = false;
}
return (bool)ViewState[ViewStateConstants.CausesValidation.ToString()];
}
set
{
ViewState[ViewStateConstants.CausesValidation.ToString()] = value;
btnDate.CausesValidation = value;
}
}
#endregion
#region Events
/// <summary>
/// A day was selected from the calendar control.
/// </summary>
public event EventHandler SelectionChanged;
protected virtual void OnSelectionChanged()
{
if (SelectionChanged != null) // only raise the event if someone is listening.
{
SelectionChanged(this, EventArgs.Empty);
}
}
#endregion
#region Event Handlers
/// <summary>
/// The +/- button was clicked.
/// </summary>
protected void btnDate_Click(object sender, System.EventArgs e)
{
if (!calDate.Visible)
{
// expand the calendar
calDate.Visible = true;
txtDate.Enabled = false;
btnDate.Text = CollapseButtonLabel;
if (DateValue != DateTime.MinValue)
{
calDate.SelectedDate = DateValue;
calDate.VisibleDate = DateValue;
}
}
else
{
// collapse the calendar
calDate.Visible = false;
txtDate.Enabled = true;
btnDate.Text = ExpandButtonLabel;
}
}
/// <summary>
/// A date was selected from the calendar.
/// </summary>
protected void calDate_SelectionChanged(object sender, System.EventArgs e)
{
calDate.Visible = false;
txtDate.Visible = true;
btnDate.Text = ExpandButtonLabel;
txtDate.Enabled = true;
txtDate.Text = calDate.SelectedDate.ToShortDateString();
OnSelectionChanged();
}
#endregion
/// <summary>
/// Builds the contents of this control.
/// </summary>
protected override void CreateChildControls()
{
btnDate.Text = ExpandButtonLabel;
btnDate.CausesValidation = CausesValidation;
txtDate.ID = "txtDate";
calDate.Visible = false;
if (ApplyDefaultStyle)
{
calDate.BackColor = System.Drawing.Color.White;
calDate.BorderColor = System.Drawing.Color.FromArgb(10066329);
calDate.CellPadding = 2;
calDate.DayNameFormat = DayNameFormat.Shortest;
calDate.Font.Name = "Verdana";
calDate.Font.Size = FontUnit.Parse("8pt");
calDate.ForeColor = System.Drawing.Color.Black;
calDate.Height = new Unit(150, UnitType.Pixel);
calDate.Width = new Unit(180, UnitType.Pixel);
calDate.DayHeaderStyle.BackColor = System.Drawing.Color.FromArgb(228, 228, 228);
calDate.DayHeaderStyle.Font.Size = FontUnit.Parse("7pt");
calDate.TitleStyle.Font.Bold = true;
calDate.WeekendDayStyle.BackColor = System.Drawing.Color.FromArgb(255, 255, 204);
}
ConnectEventHandlers();
pnlCalendar.Controls.Add(calDate);
pnlCalendar.Style["position"] = "absolute";
pnlCalendar.Style["filter"] = "alpha(opacity=95)";
pnlCalendar.Style["-moz-opacity"] = ".95";
pnlCalendar.Style["opacity"] = ".95";
pnlCalendar.Style["z-index"] = "2";
pnlCalendar.Style["background-color"] = "White";
if (CalendarPosition == CalendarDisplay.DisplayBelow)
{
pnlCalendar.Style["margin-top"] = "27px";
}
else
{
pnlCalendar.Style["display"] = "inline";
}
Controls.Add(txtDate);
Controls.Add(pnlCalendar);
Controls.Add(btnDate);
base.CreateChildControls();
}
/// <summary>
/// Render the contents of this control.
/// </summary>
/// <param name="output">The HtmlTextWriter to use.</param>
protected override void RenderContents(HtmlTextWriter output)
{
switch (CalendarPosition)
{
case CalendarDisplay.DisplayRight:
{
txtDate.RenderControl(output);
btnDate.RenderControl(output);
pnlCalendar.RenderControl(output);
break;
}
case CalendarDisplay.DisplayBelow:
{
pnlCalendar.RenderControl(output);
txtDate.RenderControl(output);
btnDate.RenderControl(output);
break;
}
}
}
/// <summary>
/// Connect event handlers to events.
/// </summary>
private void ConnectEventHandlers()
{
btnDate.Click += new System.EventHandler(btnDate_Click);
calDate.SelectionChanged += new System.EventHandler(calDate_SelectionChanged);
}
}
}
Untitled Page
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void DatePicker1_SelectionChanged(object sender, EventArgs e)
{
Label1.Text = DatePicker1.DateValue.ToShortDateString();
pnlLabel.Update();
}
}
to make the button an image you could just style it to add a background image.
btnDate.style["background-image"] ="url(images/btn.jpg)";
btnDate.text="";
In Html, an image button has a type attribute of 'image' and a src attribute used to determine the image to be displayed. So, to change a standard button to an image button you would:
buttonControl.Attributes["type"] = "image";
buttonControl.Attributes["src"] = "Foo.jpg";
EDIT
If you are trying to change the button type in the click event, then you might be required to do that via javascript client-side. You can do that by registering a start-up script in the Click event like so:
protected void Button_OnClick( object sender, EventArgs e )
{
var button = sender as Button;
if ( button == null )
return;
var jsScript = new StringBuilder();
jsScript.AppendFormat( "<script type=\"text/javascript\">" );
jsScript.AppendFormat( "var button = document.getElementById(\"{0}\"); button.type = \"image\"; button.src=\"Foo.jpg\";"
, button.ClientID);
jsScript.Append( "</script>" );
if ( !Page.ClientScript.IsStartupScriptRegistered( typeof( Page ), "resetImageButton" ) )
Page.ClientScript.RegisterStartupScript( typeof( Page ), "resetImageButton", jsScript.ToString(), false );
}
The catch is that this will only work right after the postback triggered by clicking the button. It will not survive multiple postbacks. For that, you would need to store a flag in a hidden text field or ViewState that indicates which type the button should be and then, based on that, determine whether you need to register this startup script or not.

Categories

Resources