I am trying to get an entry IsEnabled property to set based on 2 different boolean values using a MultiTrigger in Xamarin.Forms 4.6.0.726. I have also tried in the last stable 4.5 version too.
Unfortunately the Entry IsEnabled property seems to remain at whatever the Setter's value is set to (in the case, true).
I have tried the two types of BindingCondition in the below code sample. The first (uncommented) conditions are bound to the IsVisible properties of two other elements on the page. The StackLayout and the Image will toggle their visibility as expected, however the Entry IsEnabled will not changed.
The second snip of code binds directly to the values in the ViewModel, implenenting INotifyPropertyChanged but has the exact same result where the IsEnabled value does not change.
I have run out of ideas and i'm starting to wonder if this is a bug with Xamarin and MultiTriggers. There doesn't seem to be a huge amount of people using them online, and those that do I have mine set out in what seems to be the most common way in the first set of code.
<StackLayout x:Name="ButtonsStack" IsVisible="{Binding Invoice.Editable}">
<!-- Content Here -->
</StackLayout>
<Image x:Name="InvoiceImage" IsVisible="{Binding IsUploadInvoice}" />
<StackLayout Orientation="Horizontal" HorizontalOptions="End">
<Entry Text="{Binding Invoice.TotalAmount}">
<Entry.Triggers>
<MultiTrigger TargetType="Entry">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference ButtonsStack}, Path=IsVisible}" Value="True"/>
<BindingCondition Binding="{Binding Source={x:Reference InvoiceImage}, Path=IsVisible}" Value="True"/>
<!--<BindingCondition Binding="{Binding Invoice.Editable}" Value="True"/>
<BindingCondition Binding="{Binding IsUploadInvoice}" Value="True"/>-->
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiTrigger>
<Entry.Triggers>
<Entry>
</StackLayout>
As zafar said, you need to set Entry IsEnable="False" by default, when all the conditions are true, the setter makes the Entry's IsEnabled property true.
<StackLayout>
<Entry IsEnabled="False" Text="{Binding Invoice.TotalAmount}">
<Entry.Triggers>
<MultiTrigger TargetType="Entry">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference ButtonsStack}, Path=IsVisible}" Value="True" />
<BindingCondition Binding="{Binding Source={x:Reference InvoiceImage}, Path=IsVisible}" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiTrigger>
</Entry.Triggers>
</Entry>
</StackLayout>
About Multi triggers, please take a look:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/triggers
Related
I have a DataTrigger to set some styling based on the result of a validation (it is linked to a TextValidationBehaviour). It works fine, except I want it to trigger when the page loads so the user can see what they need to fill in (i.e. Entry is red until the user types something into it).
So is there any way to trigger the validation on page load?
Here is my code, but open to suggestions:
<Frame WidthRequest="350">
<StackLayout Orientation="Horizontal">
<Entry Style="{StaticResource Key=EntryInverted}" Placeholder="Last Name" Text="{Binding LastName}">
<Entry.Behaviors>
<toolkit:TextValidationBehavior x:Name="LastNameVal"
InvalidStyle="{StaticResource InvalidEntryStyle}"
Flags="ValidateOnValueChanged"
MinimumLength="2"
MaximumLength="99" />
</Entry.Behaviors>
</Entry>
</StackLayout>
<Frame.Style>
<Style TargetType="Frame" BasedOn="{StaticResource Key=EntryFrame}">
<Setter Property="BorderColor" Value="White"/>
<Style.Triggers>
<DataTrigger TargetType="Frame" Binding="{Binding Source={x:Reference LastNameVal}, Path=IsNotValid}" Value="True">
<Setter Property="BorderColor" Value="{StaticResource FrameErrorBorder}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Frame.Style>
</Frame>
You can add a trigger to the entry, such as:
<Entry >
<Entry.Triggers>
<MultiTrigger TargetType="Entry">
<MultiTrigger.Conditions>
<PropertyCondition Property="IsFocused" Value="false"/>
<PropertyCondition Property="Text" Value="{x:Null}"/>
</MultiTrigger.Conditions>
<Setter Property="BackgroundColor" Value="Red"/>
</MultiTrigger>
</Entry.Triggers>
</Entry>
The codes above will make the entry's background color show as red. And it will show the default background color when user taps the entry.
Or you want to the entry is red if the entry has no text in it. You can try the following code:
<Entry >
<Entry.Triggers>
<MultiTrigger TargetType="Entry">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={RelativeSource Self}, Path= Text.Length}" Value="0"/>
</MultiTrigger.Conditions>
<Setter Property="BackgroundColor" Value="Red"/>
</MultiTrigger>
<Trigger TargetType="Entry" Property="Text" Value="{x:Null}">
<Setter Property="BackgroundColor" Value="Red"/>
</Trigger>
</Entry.Triggers>
</Entry>
When you use the trigger above, the entry will be red until it has been typed something.
I created a customer entry which contain a label for error message
<Grid xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ExampleApp.Contols.CustomerEntry"
Title="Customer"
x:Name="AppEntry">
<Entry x:Name="ControlEntry"
BackgroundColor="{Binding BackgroundColor, Source={x:Reference AppEntry}}"/>
<Label x:Name="ConrolErrorMessage"
Text="{Binding ErrorMessage, Source={x:Reference AppEntry}}"/>
</Grid>
Now using the control, in any page.
<CustomerEntry
x:Name="Password"
BackgroundColor="Blue">
<controls:EntryControl.Behaviors>
<toolkit:TextValidationBehavior
InvalidStyle="{StaticResource InvalidEntry}"
MinimumLength="10"
MaximumLength="100" />
</controls:EntryControl.Behaviors>
</CustomerEntry>
The InvalidEntry style is simple:
<Style
x:Key="InvalidEntry"
TargetType="controls:CustomerEntry">
<Setter Property="BackgroundColor" Value="Red" />
</Style>
Now the behavior is not working. Is it possible to bind pass the behavior to the Entry of the customer control?
I try to make the entry of the customer control as follow:
<Entry x:Name="ControlEntry"
BackgroundColor="{Binding BackgroundColor, Source={x:Reference AppEntry}}"
Behaviors="{Binding Behaviors, Source={x:Reference AppEntry}}"/>
The above code didn't work.
I'm facing a problem with ToolbarItem and IsEnabled property when trying to turn it on/off from XAML using triggers. ToolbarItem doesn't support triggers, so what I do is to create a Button (a hidden one) which supports triggers and then bind Button.IsEnabled to ToolbarItem.IsEnabled; here is the sample code:
<ContentPage.ToolbarItems>
<ToolbarItem x:Name="tlbSave" Text="Save" Clicked="Handle_Clicked">
<ToolbarItem.IsEnabled>
<Binding Source="{x:Reference btnTest}" Path="IsEnabled" />
</ToolbarItem.IsEnabled>
</ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout Padding="10" VerticalOptions="CenterAndExpand">
<Entry x:Name="txtTest" HorizontalOptions="FillAndExpand" />
<Button x:Name="btnTest" Text="HIDDEN" IsEnabled="false" HorizontalOptions="FillAndExpand">
<Button.Triggers>
<MultiTrigger TargetType="Button">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference txtTest}, Path=Text.Length,
Converter={convert:IsPositiveIntegerConverter}}" Value="true" />
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiTrigger>
</Button.Triggers>
</Button>
</StackLayout>
</ContentPage.Content>
If you try this piece of code you will see how btnTest gets enable/disable when txtTest.Text has some value. But it isn't affecting tlbSave.IsEnabled property.
However, this work perfect in code behind when I set tlbSave.IsEnabled into btnText.PropertyChanged EventHandler
btnTest.IsVisible is false, I'm just showing it up for testing purposes.
Any idea about how to deal with this?
This is because of the IsEnabled property of ToolbarItem is read-only.
If you just set IsEnabled property of a toolbar item in your XAML to false or true, you will get the following exception at runtime.
System.InvalidOperationException: The BindableProperty "IsEnabled" is readonly.
And if you take a look at Microsoft's documentation, you will notice that you cannot directly set IsEnabled property of a toolbar item.
For disabling a toolbar item, the suggested way is to use a command and it's CanExecute.
I found out a way to solve this problem, at least a way better than implementing OnPropertyChange for btnTest
<ContentPage.ToolbarItems>
<ToolbarItem x:Name="tlbSave" Text="Save" Clicked="Handle_Clicked" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout Padding="10" VerticalOptions="CenterAndExpand">
<Entry x:Name="txtTest" HorizontalOptions="FillAndExpand" />
<Button x:Name="btnTest" Text="HIDDEN">
<Button.Triggers>
<MultiTrigger TargetType="Button">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference txtTest}, Path=Text.Length,
Converter={convert:IsPositiveIntegerConverter}}" Value="true" />
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiTrigger>
</Button.Triggers>
<Button.IsEnabled>
<Binding Source="{x:Reference tlbSave}" Path="IsEnabled" Mode="OneWayToSource" />
</Button.IsEnabled>
</Button>
</StackLayout>
</ContentPage.Content>
Then set btnTest.IsEnabled = false; inside constructor and everything will go as smooth as I want.
I typed "tab" and the only thing that seems to be relavent in the IntelliSense was "TabbedPage". I searched Google, and it also only showed tabbed page.
But what if I want something else above the tab? Something like this? Is this achievable in Xamarin Form? It is possible in native Android.
--------------------
Text inputs, buttons
---------------------
tabbed or swipe-able
content
--------------------
TabbedPage is a Page, is not a View so you can't include it inside another page.
On GitHub there is some TabView you can use. For example XFControls
<ctrls:TabView.TabTemplate>
<DataTemplate>
<StackLayout>
<ctrls:FontIcon Glyph="{Binding Glyph}"
FontFamily="{StaticResource font}"
FontSize="25"
Color="Gray"
HorizontalOptions="Center"
>
<ctrls:FontIcon.Triggers>
<DataTrigger TargetType="ctrls:FontIcon"
Binding="{Binding IsSelected}"
Value="True"
>
<Setter Property="Color" Value="Red" />
</DataTrigger>
</ctrls:FontIcon.Triggers>
</ctrls:FontIcon>
<Label Text="{Binding Title}"
TextColor="Gray"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
FontSize="10">
<Label.Triggers>
<DataTrigger TargetType="Label"
Binding="{Binding IsSelected}"
Value="True"
>
<Setter Property="TextColor" Value="Red" />
</DataTrigger>
</Label.Triggers>
</Label>
</StackLayout>
</DataTemplate>
</ctrls:TabView.TabTemplate>
<ctrls:TabView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Title}"
HorizontalOptions="Center" />
</DataTemplate>
</ctrls:TabView.ItemTemplate>
</ctrls:TabView>
Ive got an ObservableCollection<string> list, which is bound to a combobox. This combobox is in a datatemplate which is inside a 'DataGridTemplateColumn'.
When the datagrid is displayed (with all the rows), the column displaying this combobox works just fine. The user can select the items in the combobox, and when it's selected, the string is bound to the cell. (Just for your info: the datagrid is bound to another ObservableCollection so the cell text gets updated in that list - but i don't think it's relevant to my problem).
This is all good but a problem arises when i go to 'add' another item in the ObservableCollection<string> list that the combo box is bound to, and perform a sort. The text disappears in the 'textbox' part of some of the previously modified comboboxes. If i do not sort the list, (just add a new value) everything is fine.
I think what is happenning is that the binding gets screwed up when i re-sort the list. Because the list has 'changed', the order of the strings in the list are now different, so the binding doesn't know what to display.
How can i get this to work? The previously selected comboboxes's text disappears when i re-sort the ObservableCollection<string> list.
My <DataGridTemplateColumn> containing the combo box is:
<WpfToolkit:DataGridTemplateColumn
Header="Category" Width="1*"
CellTemplate="{StaticResource ComboBoxCellDataTemplate}"
CellEditingTemplate="{StaticResource ComboBoxCellEditingTemplate}"/>
...and the related DataTemplates are:
<DataTemplate x:Key="ComboBoxCellDataTemplate">
<Label x:Name="lblCombo" Content="{Binding Category}" Style="{StaticResource BaseLabelCellStyle}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Categories, Mode=TwoWay}" Value="Both">
<Setter TargetName="lblCombo" Property="IsEnabled" Value="False" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate x:Key="ComboBoxCellEditingTemplate">
<!-- min=60, max=600 also, add in a 'specific' scalar value -->
<ComboBox
x:Name="comboBox"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Categories, Mode=TwoWay}"
SelectedItem="{Binding Category}" LostFocus="comboBox_LostFocus" IsEditable="True" PreviewKeyDown="comboBox_PreviewKeyDown" MaxDropDownHeight="100" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Enabled}" Value="False">
<Setter TargetName="comboBox" Property="IsEnabled" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Categories, Mode=TwoWay}" Value="Both">
<Setter TargetName="comboBox" Property="IsEnabled" Value="True" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Note that the majority of this code is by Samuel Moura at http://sweux.com/blogs/smoura/index.php/tag/datagridcolumn/
Hey I think I have a solution for you. Just add the following line to your Datagrid definition
SelectionUnit="Cell"
I dunno how, it worked for me:) Just give it a try and let me know if it helps.