Xamarinforms DatePicker how to change textbox's fontsize - xaml

I have an app created with : xamarinforms -prism - materialDesign
I wold like to change the default the inner textbox's fontsize of the DatePicker, TimePicker controls, because it is to big, I 'd like a smaller text, my code is:
<DatePicker Date="{Binding mydate,Mode=TwoWay}" FontSize="10"/>
No matter the size I set to FontSize, it doesn't work at all, the same happens with TimePicker,
any help will be great,
thanks in advance

You could use CustomRender to achieve this:
For Android:
[assembly: ExportRenderer(typeof(Xamarin.Forms.DatePicker), typeof(AndroidDatePickerRenderer))]
namespace YourNameSpace
{
class AndroidDatePickerRenderer: DatePickerRenderer
{
public AndroidDatePickerRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.DatePicker> e)
{
base.OnElementChanged(e);
if(Control !=null){
Control.TextSize = 10; //change the size
}
}
}
}
For ios:
[assembly: ExportRenderer(typeof(Xamarin.Forms.DatePicker), typeof(IosDatePickerRenderer))]
namespace YourNameSpace
{
class IosDatePickerRenderer: DatePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
base.OnElementChanged(e);
if(Control != null)
{
Control.Font = UIFont.SystemFontOfSize(10); //change the size
}
}
}
}

if anyone goes through this, finally works just using this
<DatePicker Date="{Binding mydate,Mode=TwoWay}"
FontSize="Body" TextColor="{DynamicResource Key=QuaternaryColor}"/>
the same for the timePicker
thanks for the help

Related

How to create multiple custom renderer with same type?

I wanted to create a page render with ContentPage type. I created so in android and it is working but in IOS there has custom page renderer with same type (ContentPage). It can be removed as this was from nuget package and working on different context.
Here is my code
[assembly: ExportRenderer(typeof(ContentPage), typeof(CustomPageRenderer))]
namespace AlwinInvoiceGenerator.IOS
{
using CoreGraphics;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
public class CustomPageRenderer : PageRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (Element == null || !(Element is ContentPage basePage) || basePage.BindingContext == null ||
!(basePage.BindingContext is BaseViewModel baseViewModel))
{
return;
}
SetCustomBackButton(baseViewModel);
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
OverrideUserInterfaceStyle = UIUserInterfaceStyle.Light;
}
private void SetCustomBackButton(BaseViewModel baseViewModel)
{
var fakeButton = new UIButton {Frame = new CGRect(0, 0, 50, 40), BackgroundColor = UIColor.Clear};
fakeButton.TouchDown += (sender, e) =>
{
baseViewModel.OnBackButtonClicked();
};
NavigationController?.NavigationBar.AddSubview(fakeButton);
}
}
It seems it not registering and that is why not calling.
I have another page renderer that is register in assembly
[assembly: ExportRenderer(typeof(ContentPage), typeof(IOSToolbarExtensions.iOS.Renderers.IOSToolbarExtensionsContentPageRenderer), Priority = short.MaxValue)]
If I removed this line then above code is working but not two in the same time.
Please help
Same type seems not working for multiple renderer.
I have created sub type of my custom renderer and override the methods which needed to. It is working well

Multiple properties with name 'BadgeView.Shared.CircleView.CornerRadius' found - Exception

I am using BadgeView plugin in Xamarin.Forms with TitleView but getting exception
Xamarin.Forms.Xaml.XamlParseException: Position 8:81. Multiple
properties with name 'BadgeView.Shared.CircleView.CornerRadius' found.
This is my code
<NavigationPage.TitleView>
<StackLayout Orientation="Horizontal" VerticalOptions="End" Spacing="10">
<Image Source="bell.png" HorizontalOptions="Center" VerticalOptions="Center"></Image>
<badge:BadgeView Text="2" BadgeColor="Yellow" VerticalOptions="End" HorizontalOptions="Start"></badge:BadgeView>
</StackLayout>
</NavigationPage.TitleView>
This is a bug in the plugin which is marked in the issues section in the plugin's Github
And there is a workaround that one guy has added but I am not sure if that would work for you, as shown here what he did was
CircleView.cs
Note: Comment the CornerRadius property.
public class CircleView : BoxView
{
//public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(double), typeof(CircleView), 0.0);
//public double CornerRadius
//{
// get { return (double)GetValue(CornerRadiusProperty); }
// set { SetValue(CornerRadiusProperty, value); }
//}
}
For Android:
CircleViewRenderer.cs
Note: Added hard-coded value for CornerRadius (16)
public class CircleViewRenderer : BoxRenderer
{
private float _cornerRadius;
private RectF _bounds;
private Path _path;
public CircleViewRenderer(Context context)
: base(context)
{
}
public static void Initialize() { }
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
if (Element == null)
{
return;
}
var element = (CircleView)Element;
_cornerRadius = TypedValue.ApplyDimension(ComplexUnitType.Dip, (float)16, Context.Resources.DisplayMetrics);
}
protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
{
base.OnSizeChanged(w, h, oldw, oldh);
if (w != oldw && h != oldh)
{
_bounds = new RectF(0, 0, w, h);
}
_path = new Path();
_path.Reset();
_path.AddRoundRect(_bounds, _cornerRadius, _cornerRadius, Path.Direction.Cw);
_path.Close();
}
public override void Draw(Canvas canvas)
{
canvas.Save();
canvas.ClipPath(_path);
base.Draw(canvas);
canvas.Restore();
}
}
For iOS:
CircleViewRenderer.cs
Note: Added hard-coded value for CornerRadius (16)
iOS code is not tested yet.
public class CircleViewRenderer : BoxRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
if (Element == null)
return;
Layer.MasksToBounds = true;
//Layer.CornerRadius = (float)((CircleView)Element).CornerRadius / 2.0f;
Layer.CornerRadius = (float)(16) / 2.0f;
}
}

How to set TextColor Xamarin.Forms TableSection?

I am a fan of doing as much as possible in xaml, I have aTableView` with a TableSection.
<TableView Intent="Menu">
<TableRoot>
<TableSection Title="Test Section" TextColor="#FFFFFF">
<TextCell Text="Test Item" TextColor="#FFFFFF"/>
</TableSection>
</TableRoot>
</TableView>
For TextCell TextColor="#FFFFFF" seems to work, however whenever I use this attribute on a TableSection I get this:
An unhandled exception occurred.
Is it possible to change the color of the TableSection with xaml?
Custom Renderers! I have two blog posts on this here:
Android:Xamarin.Forms TableView Section Custom Header on Android
iOS: Xamarin.Forms TableView Section Custom Header on iOS
Basically, create a custom view that inherits TableView, then custom renderers that implement custom TableViewModelRenderer. From there you can override methods to get the header view for the section title.
Here's what that might look like for Android:
public class ColoredTableViewRenderer : TableViewRenderer
{
protected override TableViewModelRenderer GetModelRenderer(Android.Widget.ListView listView, TableView view)
{
return new CustomHeaderTableViewModelRenderer(Context, listView, view);
}
private class CustomHeaderTableViewModelRenderer : TableViewModelRenderer
{
private readonly ColoredTableView _coloredTableView;
public CustomHeaderTableViewModelRenderer(Context context, Android.Widget.ListView listView, TableView view) : base(context, listView, view)
{
_coloredTableView = view as ColoredTableView;
}
public override Android.Views.View GetView(int position, Android.Views.View convertView, ViewGroup parent)
{
var view = base.GetView(position, convertView, parent);
var element = GetCellForPosition(position);
// section header will be a TextCell
if (element.GetType() == typeof(TextCell))
{
try
{
// Get the textView of the actual layout
var textView = ((((view as LinearLayout).GetChildAt(0) as LinearLayout).GetChildAt(1) as LinearLayout).GetChildAt(0) as TextView);
// get the divider below the header
var divider = (view as LinearLayout).GetChildAt(1);
// Set the color
textView.SetTextColor(_coloredTableView.GroupHeaderColor.ToAndroid());
textView.TextAlignment = Android.Views.TextAlignment.Center;
textView.Gravity = GravityFlags.CenterHorizontal;
divider.SetBackgroundColor(_coloredTableView.GroupHeaderColor.ToAndroid());
}
catch (Exception) { }
}
return view;
}
}
}
And the on iOS:
public class ColoredTableViewRenderer : TableViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
{
base.OnElementChanged(e);
if (Control == null)
return;
var coloredTableView = Element as ColoredTableView;
tableView.WeakDelegate = new CustomHeaderTableModelRenderer(coloredTableView);
}
private class CustomHeaderTableModelRenderer : UnEvenTableViewModelRenderer
{
private readonly ColoredTableView _coloredTableView;
public CustomHeaderTableModelRenderer(TableView model) : base(model)
{
_coloredTableView = model as ColoredTableView;
}
public override UIView GetViewForHeader(UITableView tableView, nint section)
{
return new UILabel()
{
Text = TitleForHeader(tableView, section),
TextColor = _coloredTableView.GroupHeaderColor.ToUIColor(),
TextAlignment = UITextAlignment.Center
};
}
}
}
According to the official documentation for TableSection you are out of luck. I'm afraid you would have to write a custom renderer for a subclass of the TableSection class and expose an extra property of type Xamarin.Forms.Color. Then you would be able to set the color from XAML.
You can set this color in the base theme (may apply to other widgets too)
In /Resources/values/styles.xml
<style name="MainTheme.Base" parent="Theme.AppCompat.Light">
<item name="colorAccent">#434857</item>
For Individual Section Titles, the TextColor property now seems to work correctly:
<TableView Intent="Settings">
<TableRoot>
<TableSection Title="App Settings" TextColor="Red">

What is the correct property name to notify when binding to an element in an Observable collection directly?

I have a binding of the following form in XAML,
Title="{Binding SelectedNewsItems[0].Title}"
Note that it refers to a particular element in the SelectedNewsItems which is an ObservableCollection. (I have a collection of nine buttons of various sizes, each styled, and sized differently and so a more standard ListView is not appropriate.)
When I reassign SelectedNewsItems I raise a PropertyChanged event for SelectedNewsItems, however, this does not appear to cause the bindings to update for the individual bound elements and their properties. I have tried the following,
public ObservableCollection<NewsItem> _selectedNewsItems;
public ObservableCollection<NewsItem> SelectedNewsItems
{
get
{
return this._selectedNewsItems;
}
set
{
if (this._selectedNewsItems != value)
{
this._selectedNewsItems = value;
this.NotifyPropertyChanged();
for (int i = 0; i < this._selectedNewsItems.Count; i++)
{
this.NotifyPropertyChanged(String.Format("SelectedNewsItems[{0}].Content", i));
this.NotifyPropertyChanged(String.Format("SelectedNewsItems[{0}].Title", i));
this.NotifyPropertyChanged(String.Format("SelectedNewsItems[{0}].Id", i));
this.NotifyPropertyChanged(String.Format("SelectedNewsItems[{0}].Image", i));
}
}
}
}
Hmm, I cannot exacly say where your code is wrong (as I see only part of it), but maybe you haven't set your DataContex or something else. For the purpose of research I've made simple example, which works quite fine. Take a look at it and maybe it will help you:
In Xaml:
<Button x:Name="first" VerticalAlignment="Top" Content="{Binding SelectedNewsItems[0].Name}" Grid.Row="0"/>
<Button x:Name="second" VerticalAlignment="Center" Content="{Binding SelectedNewsItems[1].Name}" Grid.Row="1"/>
In code behind (I put all the code - yeah quite a lot of, but I cannot guess what is wrong with your code):
public class NewsItem
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
public partial class MainPage : PhoneApplicationPage, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseProperty(string property = null)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
private ObservableCollection<NewsItem> _selectedNewsItems = new ObservableCollection<NewsItem>();
public ObservableCollection<NewsItem> SelectedNewsItems
{
get
{
return this._selectedNewsItems;
}
set
{
if (this._selectedNewsItems != value)
{
this._selectedNewsItems = value;
this.RaiseProperty();
for (int i = 0; i < this._selectedNewsItems.Count; i++)
{
this.RaiseProperty(String.Format("SelectedNewsItems[{0}].Name", i));
}
}
}
}
public MainPage()
{
NewsItem firstT = new NewsItem() { Name = "First" };
NewsItem secondT = new NewsItem() { Name = "Second" };
SelectedNewsItems.Add(firstT);
SelectedNewsItems.Add(secondT);
InitializeComponent();
this.DataContext = this;
first.Click += first_Click;
second.Click += second_Click;
}
private void first_Click(object sender, RoutedEventArgs e)
{
NewsItem change = new NewsItem() { Name = "Changed by First" };
SelectedNewsItems[1] = change;
}
private void second_Click(object sender, RoutedEventArgs e)
{
NewsItem change = new NewsItem() { Name = "Changed by Second" };
SelectedNewsItems[0] = change;
}
}
As I click on buttons the bindigs work, so maybe it will help you.

How to databind control height to another control's height?

I'm trying to have 2 controls have the same height. Can I do it with XAML only?
If I did something like <Canvas Height="{Binding Height, ElementName=AnotherControl}" /> it doesn't actually do anything and the height goes to zero. The Output panel doesn't complain about any binding errors so AnotherControl.Height really exists. I tried binding to ActualHeight but it doesn't do anything either.
Anything else I missed?
My guess is that you AnotherControl is not explicitly given a Height. Unfortunately, in WinRT (unlike WPF, but the same as Silverlight), ActualWidth and ActualHeight are what are known as "calculated properties". This means that a property changed event doesn't internally get raised when they change. As a result, binding to them is not reliable, and as you've noticed, it wouldn't quite work.
Side note: it may work from time to time, but that is purely because of the timing of the get call the binding framework makes to ActualHeight.
So as it stands, you cannot do it with XAML only. You have to handle the ActualControl.SizeChanged event in code-behind, and set the Height to AnotherControl.ActualHeight explicitly.
As Kshitij Mehta mentioned, binding to ActualHeight and ActualWidth in WinRT isnt reliable. But there is a nice work-around, where you dont have to use the SizeChanged-Event:
Add this class:
public class ActualSizePropertyProxy : FrameworkElement, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public FrameworkElement Element
{
get { return (FrameworkElement)GetValue(ElementProperty); }
set { SetValue(ElementProperty, value); }
}
public double ActualHeightValue
{
get { return Element == null ? 0 : Element.ActualHeight; }
}
public double ActualWidthValue
{
get { return Element == null ? 0 : Element.ActualWidth; }
}
public static readonly DependencyProperty ElementProperty =
DependencyProperty.Register("Element", typeof(FrameworkElement), typeof(ActualSizePropertyProxy),
new PropertyMetadata(null, OnElementPropertyChanged));
private static void OnElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ActualSizePropertyProxy)d).OnElementChanged(e);
}
private void OnElementChanged(DependencyPropertyChangedEventArgs e)
{
FrameworkElement oldElement = (FrameworkElement)e.OldValue;
FrameworkElement newElement = (FrameworkElement)e.NewValue;
newElement.SizeChanged += new SizeChangedEventHandler(Element_SizeChanged);
if (oldElement != null)
{
oldElement.SizeChanged -= new SizeChangedEventHandler(Element_SizeChanged);
}
NotifyPropChange();
}
private void Element_SizeChanged(object sender, SizeChangedEventArgs e)
{
NotifyPropChange();
}
private void NotifyPropChange()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ActualWidthValue"));
PropertyChanged(this, new PropertyChangedEventArgs("ActualHeightValue"));
}
}
}
Place it in the resources:
<UserControl.Resources>
<c:ActualSizePropertyProxy Element="{Binding ElementName=YourElement}" x:Name="proxy" />
</UserControl.Resources>
And bind to its properties:
<TextBlock x:Name="tb1" Text="{Binding ActualWidthValue, ElementName=proxy}" />
This Question is very old, but here is my solution.
You can use this Code
<!--First Button-->
<Button x:Name="button1" Height="50" Width="100"/>
<!--Second Button-->
<Button x:Name="button2" Height="50" Width="{Binding ElementName=button1, Path=Width}"/>
I've tested it on my Windows / Windows Phone 8.1 Device and it workes great.