Xamarin Forms Subclass with Modified Bindable Property Defaults - xamarin.forms

I am trying to get a subclass of the Xamarin Forms "Label" class. In my subclass, among a lot of other changes, I want to have a different default value for some bindable properties (such as FontSize and FontAttributes). However, if I set these in the constructor, it seems that Style specifiers won't override these, presumably because the bindings are already noticing that they are using non-default values. Is there a way to specify in a subclass that you want to use different default values in a bindable property?
class MyCustomLabel : Label {
public MyCustomLabel() {
FontSize=20;
}
}
<ResourceDictionary>
<Style x:Key="Superbig" TargetType="MyCustomLabel">
<Setter Property="FontSize" Value="3" />
</Style>
</ResourceDictionary>
<MyCustomLabel Style="{StaticResource Superbig}" Text="Hi There!" />
Here, the Superbig style is not being applied because I am setting the new default value in the constructor. Therefore, I was hoping either (a) there was some other way to set a new default value, or (b) there was some other way to set a style so it overrode any value that was already set.

Unfortunately, BindableProperty doesn't seem to support OverrideMetadata like DependencyProperty does. Here's two way to achieve this.
1) Set a default Style for your MyCustomLabel object (XAML)
<ResourceDictionary>
<!--Default style-->
<Style TargetType="local:MyCustomLabel">
<Setter Property="FontSize" Value="10" />
</Style>
<!--Superbig style-->
<Style x:Key="Superbig" TargetType="local:MyCustomLabel">
<Setter Property="FontSize" Value="40" />
</Style>
</ResourceDictionary>
2) Create a new FontSize BindableProperty (C#)
public class MyCustomLabel : Label
{
public MyCustomLabel()
{
base.SetBinding(Label.FontSizeProperty, new Binding(nameof(FontSize))
{
Source = this,
Mode = BindingMode.OneWay
});
}
//Don't forget the "new" keyword
public new double FontSize
{
get { return (double)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
//Don't forget the "new" keyword
public static readonly new BindableProperty FontSizeProperty =
BindableProperty.Create(nameof(FontSize), typeof(double), typeof(MyCustomLabel), 40.0);
}

Related

Time Need to validate for timepicker in xamarin forms

I need to validate a selected time from the time picker. So if the selected time is not valid then I need to display a toast message.
Which event will trigger if time will select from the time picker?
And I don't want to bind the time if the selected time is not valid based on my condition.
XAML Code:
<controls:DatePickerCustom FontSize="14" HeightRequest="35" Date="{Binding CustomDate,Mode=TwoWay}" MaximumDate="{Binding MaximumDate}" FontFamily="Segoe UI" Format="D" IsEnabled="{Binding IsDatePickerEnable}">
<DatePicker.Triggers>
<DataTrigger TargetType="DatePicker" Binding="{Binding IsCustomSelected}" Value="true">
<Setter Property="TextColor" Value="{DynamicResource HeadingTextColor}" />
<Setter Property="TextColor" Value="{DynamicResource HeadingTextColor}" />
</DataTrigger>
</DatePicker.Triggers>
</controls:DatePickerCustom>
Handle this in your "setter" for property CustomDate.
In a setter, the new value is value. If value isn't valid, Show the toast message. Return from setter, without changing the internal field that holds the value, and without calling OnPropertyChanged.
If you need more information, please show the code that declares property CustomDate.
It that property is currently an "auto-property", convert it to a "full-property" - this will have a "backing field", and gives you a place to do the test, and reject the change.
You could listen the PropertyChanged event of TimePicker,then match the Time property.
For example:
<TimePicker PropertyChanged="TimePicker_PropertyChanged"></TimePicker>
private void TimePicker_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
TimePicker timePicker = sender as TimePicker;
if (e.PropertyName.Equals("Time") )
{
var time = timePicker.Time;
if (time.Minutes == 10) // your condition
{
}
else
{
DisplayAlert("Warning", "This is a validate time", "Cancel");
}
}
}

how to change custom render label textcolor inside list view in xamarin forms

We are developing a small application, we have created dashboard using custom render but I can’t change label text color. it is default showing like lable text color white, list view background color It will come via api so that if it is coming white background then label text color is not able to see. Here I have attached the code below. Give me suggestions to resolve this issue
Menucontrol custom render
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create<MenuControl, IEnumerable>(
view => view.ItemsSource,
null,
BindingMode.TwoWay,
null,
propertyChanged: (bindableObject, oldValue, newValue) =>
{
((MenuControl)bindableObject).ItemsSourceChanged(bindableObject, oldValue, newValue);
}
);
public IEnumerable ItemsSource
{
get
{
return (IEnumerable)GetValue(ItemsSourceProperty);
}
set
{
SetValue(ItemsSourceProperty, value);
}
}
Add a Data Trigger
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor={Binding BGColor}>
<Label TextColor="White">
<Label.Triggers>
<!--(or Value ="White" depends on binding value Xamarin.Color or string) -->
<DataTrigger TargetType="Label" Binding={Binding BGColor} Value="#FFFFFF">
<Setter Property="TextColor" Value="Red"/>
<!--(or your color) -->
</DataTrigger>
</Label.Triggers>
</Label>
</StackLayout>
<ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
So when your BGColor(or any property you bind to color) property is something you think that can clash(for e.g. white) with your text color(e.g. also white) use data triggers. You can create multiple but if there are more than 3 or 4 I would advise you to use converters in that case.

Change Xamarin.Forms button clicked colour

I am working on a Xamarin.Forms UWP application and I wanted to change my button's background colour when pressed. I have been searching the web and the most straightforward way I could find is this:
private void Button_OnClicked(object s, EventArgs e)
{
var b = (Button) s;
var originalColour = b.BackgroundColor;
b.BackgroundColor = Color.DarkOrange;
Device.StartTimer(TimeSpan.FromSeconds(0.25), () =>
{
b.BackgroundColor = originalColour;
return false;
});
}
However, personally, I am not liking this approach very much. How can this be done better?
An EventTrigger solution in XAML:
Implement the following in MyAssembly, e.g. the portable assembly containing App.xaml:
using Xamarin.Forms;
namespace MyNamespace
{
public class ButtonTriggerAction : TriggerAction<VisualElement>
{
public Color BackgroundColor { get; set; }
protected override void Invoke(VisualElement visual)
{
var button = visual as Button;
if (button == null) return;
if (BackgroundColor != null) button.BackgroundColor = BackgroundColor;
}
}
}
XAML:
xmlns:local="clr-namespace:MyNamespace;assembly=MyAssembly"
...
<Button Text="EventTrigger">
<Button.Triggers>
<EventTrigger Event="Pressed">
<local:ButtonTriggerAction BackgroundColor="Red" />
</EventTrigger>
<EventTrigger Event="Released">
<local:ButtonTriggerAction BackgroundColor="Default" />
</EventTrigger>
</Button.Triggers>
</Button>
a more natural and cleaner way to do it would be using VisualStateManager
<Button Text="Click Me!">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Green" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Button>
you can read more about in here.
This is the simplest solution but of course not very clean.
THe problem here is that each platform implements the "pressed" state differently and Xamarin.Forms doesn't have any built-in way how to handle this.
In case of UWP, you have two options. First, you can create a new default button style that will be used throughout your app. You can find the default style here, and just copy it, modify the Pressed VisualState and add is as a default resource:
<Style TargetType="Button">
<!-- ... your style -->
</Style>
However, if the pressed button color should be applied only in some places, you should rather create a new view that derives from button and uses a custom renderer on UWP that applies a custom style in the OnElementChanged event handler:
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (this.Element != null)
{
this.Control.Style = ( Style )Application.Current.Resources["CustomButtonStyle"];
}
}
Other platforms will have similar solutions as well, but you will definitely have to implement them in such platform-specific way, probably using the custom renderers.
For more information on custom renderers see the documentation. You may also find some inspiration in Xamarin.Forms Labs repository.

Binding to selection of ViewModel

I'm trying to bind my ListBox to a selection of my ViewModel, because I have multiple ListBoxes in a Pivot and I don't want to type out the entire Page for each property. To illustrate my issue, here's a small sample:
XAML:
<DataTemplate x:Key="PropertyTemplate">
<StackPanel>
<TextBlock Text="{Binding Label}" />
<TextBlock Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
<controls:Pivot>
<controls:PivotItem>
<ListBox ItemsSource="{Binding PropertySelectionOne}" ItemTemplate="{StaticResource PropertyTemplate}" />
</controls:PivotItem>
<controls:PivotItem>
<ListBox ItemsSource="{Binding PropertySelectionTwo}" ItemTemplate="{StaticResource PropertyTemplate}" />
</controls:PivotItem>
</controls:Pivot>
ViewModel:
public class SomeViewModel
{
private Property _propOne;
public Property PropOne
{
get { return _propOne; }
set { _propOne = value; NotifyPropertyChanged("PropOne"); }
}
private Property _propTwo;
public Property PropTwo
{
get { return _propTwo; }
set { _propTwo = value; NotifyPropertyChanged("PropTwo"); }
}
private Property _propThree;
public Property PropThree
{
get { return _propThree; }
set { _propThree = value; NotifyPropertyChanged("PropThree"); }
}
}
So basically I want to bind my ListBoxes to PropertySelectionOne and PropertySelectionTwo, which would contain references to a selection of the properties in my ViewModel. For instance, PropertySelectionOne could include PropOne and PropTwo and PropertySelectionTwo could include PropTwo and PropThree.
Is there a simple way to "group" these properties to a new property to bind against without changing the architecture of my application?
Thanks
If you've got different properties to be displayed from the same date type in different list boxes, then arguably you need to split your view model, but you say you don't want to change the architecture of your application, which is your choice.
So, what you need to do is to provide a different ItemTemplate for each ListBox that defines which properties and how you want to display in each ListBox. Then you can bind the ItemsSource for all of the list boxes to the same data source but they will present different properties according to the ItemTemplate.
Not sure how familiar you are with these concepts, but you know that ItemsSource needs to be a collection of your data instances (SomeViewModel?), right?

How add a global "IsReadOnly" style to all DataGridTextColumns

I currently have a ResourceDictionary file for my WPF application, which pretty much adds every style that I could possibly want throughout all of my application's DataGrids.
Except one.
How can I add a global "IsReadOnly" setter, for all of my DataGrid's DataGridTextColumn columns ?
Basically, I use a few DataGrids, and if I want to display read-only data in a particular column, I'll just display the data using a DataGridTextColumn:
<WPFtoolkit:DataGridTextColumn Binding="{Binding Path=DOB,StringFormat='dd/MMM/yyyy'}" Header="DOB" Width="120" />
However, if I have a column which has editable data, then I'll use a DataGridTemplateColumn instead.
<WPFtoolkit:DataGridTemplateColumn xHeader="Department Name" >
<WPFtoolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="175"
ItemsSource="{Binding Source={StaticResource AllDepartmentsDataSource}}"
SelectedValue="{Binding DepartmentID}" SelectedValuePath="DepartmentID" DisplayMemberPath="DepartmentName"
VerticalAlignment="Center"
>
</ComboBox>
</DataTemplate>
</WPFtoolkit::DataGridTemplateColumn.CellTemplate>
</tWPFtoolkit:DataGridTemplateColumn>
The problem is, for every one of my DataGridTextColumns, I specifically need to add the IsReadOnly parameter, otherwise the user can (incorrectly) edit that data:
<WPFtoolkit:DataGridTextColumn IsReadOnly="True" Binding="{Binding Path=DOB,StringFormat='dd/MMM/yyyy'}" Header="DOB" Width="120" />
Is there a way to add this "IsReadOnly" setting globally, in the ResourceDictionary file, to all of my DataGridTextColumns...?
I can add global styles to DataGrid, DataGridColumnHeader, DataGridRow, and DataGridCell, but if I try to define a style with a TargetType of DataGridTextColumn, then Visual Studio complains that DataGridTextColumn is not derived from a FrameworkElement or FrameworkContentElement.
<Style TargetType="{x:Type WPFToolkit:DataGridTextColumn}">
<Setter Property="IsReadOnly" Value="True"/>
</Style>
I can add IsReadOnly to the Grid style, but this makes all columns uneditable !
Can anyone think of an quick and easy way to add this simple property to the DataGridTextColumns ?
Update:
My solution has been (reluctantly) to add a Loaded handler to each of my DataGrids, which runs this code:
void grdGrid_Loaded(object sender, RoutedEventArgs e)
{
DataGrid dg = (DataGrid)sender;
foreach (DataGridColumn col in dg.Columns)
{
DataGridTextColumn textCol = col as DataGridTextColumn;
if (textCol != null)
{
textCol.IsReadOnly = true;
}
else
{
// This DataGridColumn isn't of type "DataGridTextColumn", so do nothing.
}
}
}
You could, of course, put this in your own DataGrid-inherited control, rather than repeating it for each of your DataGrids.
(Sigh.)
Why didn't MS make IsReadOnly an attachable property..? It would've made life so much easier!

Resources