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.
Related
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");
}
}
}
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.
I have been struggling to make my inventory lists maintain consistent highlighted items when using a SearchBar or sometimes navigating away from the page. I see this great looking sample from the MS Docs' "Xamarin.Forms CollectionView Selection : Multiple Pre-Selection"
And I took this SearchBar sample:
https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/userinterface-searchbardemos/
And I have modified the Xaml w/MVVM page to look more like my project: https://github.com/BullCityCabinets/MyXFMultiSelectExample-2001005
The frustrating thing is... the modified sample works! I gather from #Jason that I am completely replacing the view's ItemSource, and the binding don't know how to identify the new instances of the object. To compare:
The MS Docs Sample data source is a public static ObservableCollection
public static class DataService
{
public static ObservableCollection<string> Fruits { get; } = new ObservableCollection<string>
{ "Akee", "Apple", etc...
and uses this call when a search is made:
public static ObservableCollection<string> GetSearchResults(string queryString)
{
var normalizedQuery = queryString?.ToLower() ?? "";
var myList = Fruits.Where(f => f.ToLowerInvariant().Contains(normalizedQuery)).ToList();
var myOC = new ObservableCollection<string>();
foreach (var f in myList)
{ myOC.Add(f); }
return myOC;
}
My data source is a single SQLite table of about 350 objects, and on searching I call this:
public Task<List<MyInventoryClass>> GetInventoryByQuery(string query)
{
return Db.Table<MyInventoryClass>()
.Where(i =>
i.Name1.ToLower().Contains(query.ToLower()) == true)
.ToListAsync();
}
There is an auto-incrementing Id property on the objects, so I have something to search for... I'm just not sure how to re-establish the link between SelectedItems and the new list.
Should I be making a new call to the SQLite table every time there is a search?
Should I, instead, save all 350 items in a static property at startup, then display queries from that? What if there were 3,500 objects, surely that's not the best solution, is it?
How and where do you insert a foreach to join the existing SelectedItems list to the freshly queried CollectionView ItemSource? The objects in the SelectedItems list have unique Id properties, so there is something identify the items with, aside from index numbers (seen in the MS Docs Pre-Selection Sample).
When I want to remove the highlight from a CollectionView after selection, always add this to the page if you use a GridItemsLayout
<ContentPage.Resources>
<Style TargetType="Grid">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonState">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ContentPage.Resources>
I have an custom control with following DP:
public FrameworkElement NoResultContent
{
get { return (FrameworkElement)GetValue(NoResultContentProperty); }
set { SetValue(NoResultContentProperty, value); }
}
public static readonly DependencyProperty NoResultContentProperty =
DependencyProperty.Register("NoResultContent", typeof(FrameworkElement), typeof(AdvancedAutoCompleteBox), new PropertyMetadata(null));
The ControlTemplate of my custom control shows this DP in a ContentControl:
<ContentControl Content="{TemplateBinding NoResultContent}" />
It's used in a view to provide arbitrary functions:
<Controls:AdvancedAutoCompleteBox
x:Name="Box"
ItemsSource="{Binding Persons}"
SelectedItem="{Binding SelectedPerson}"
Watermark="Search here">
<Controls:AdvancedAutoCompleteBox.NoResultContent>
<StackPanel>
<Button
Content="Add by ICommand"
Command="{Binding AddPerson}" />
<Button
x:Name="AddPerson"
Content="Add by Caliburn" />
</StackPanel>
</Controls:AdvancedAutoCompleteBox.NoResultContent>
</Controls:AdvancedAutoCompleteBox>
The Command-Binding to a ICommand works just fine. Buy why does it not work with Caliburn.Micro?
I also tried to attach the context to the second Button manually by cal:Bind.Model
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);
}