Skip to content

Commit

Permalink
Adds support for the barcode input field (#583)
Browse files Browse the repository at this point in the history
* Adds support for the barcode input field
  • Loading branch information
dotMorten authored Jul 16, 2024
1 parent 0673608 commit ac1bc3e
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 16 deletions.
43 changes: 43 additions & 0 deletions src/Toolkit/Toolkit.Maui/Internal/CalciteImageButton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// /*******************************************************************************
// * Copyright 2012-2018 Esri
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// ******************************************************************************/

namespace Esri.ArcGISRuntime.Toolkit.Maui.Internal
{
internal sealed class CalciteImageButton : ImageButton
{
private readonly string _glyph;
public CalciteImageButton(string glyph)
{
_glyph = glyph;
Source = new FontImageSource() { Glyph = _glyph, FontFamily = "calcite-ui-icons-24", Color = Color };
this.SetAppThemeColor(ColorProperty, Colors.Black, Colors.White);
}

public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}

public static readonly BindableProperty ColorProperty = BindableProperty.Create(nameof(Color), typeof(Color), typeof(CalciteImageButton), Colors.Black,
propertyChanged: (s, oldValue, newValue) => ((CalciteImageButton)s).SetColor());

private void SetColor()
{
((FontImageSource)Source).Color = Color;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
</Style>
<Style TargetType="{x:Type primitives:TextFormInputView}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type primitives:TextFormInputView}">
Expand Down Expand Up @@ -156,14 +158,29 @@
<TextBlock Text="/" />
<TextBlock Text="{Binding MaxLength, ElementName=TextInput}" />
</StackPanel>
<TextBox x:Name="TextInput" MaxLines="{TemplateBinding MaxLines}" MinLines="{TemplateBinding MinLines}" />
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox x:Name="TextInput" MaxLines="{TemplateBinding MaxLines}" MinLines="{TemplateBinding MinLines}" BorderThickness="0" />
<TextBlock Text="!" Background="Transparent" Width="10" Foreground="Red" Visibility="Collapsed" HorizontalAlignment="Right" VerticalAlignment="Center" x:Name="ErrorInfo" Grid.Column="1" />
<Button Content="&#xED14;" FontFamily="Segoe MDL2 Assets" Background="Transparent" Grid.Column="2"
Padding="8" Margin="-8,-8,-5,-7" BorderThickness="0" Visibility="Collapsed" HorizontalAlignment="Right" VerticalAlignment="Center" x:Name="BarcodeButton" />
</Grid>
</Border>
<TextBlock x:Name="ReadOnlyText" Visibility="Collapsed" />
<Border x:Name="ErrorBorder" BorderThickness="1" BorderBrush="Red" Visibility="Collapsed" />
<TextBlock Text="!" Background="Transparent" Width="10" Foreground="Red" Visibility="Collapsed" HorizontalAlignment="Right" VerticalAlignment="Center" x:Name="ErrorInfo" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ShowBarcodeScanner" Value="true">
<Setter TargetName="BarcodeButton" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="TextInput" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="BarcodeButton" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="ReadOnlyText" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsEnabled" Value="true">
Expand All @@ -176,6 +193,7 @@
<Trigger Property="ShowCharacterCount" Value="true">
<Setter TargetName="CharacterCountPanel" Property="Visibility" Value="Visible"/>
</Trigger>

</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
Expand Down Expand Up @@ -219,6 +237,13 @@
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="BarcodeScannerFormInputTemplate">
<Setter.Value>
<DataTemplate>
<primitives:TextFormInputView Element="{Binding}" IsEnabled="{Binding IsEditable}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="DateTimePickerFormInputTemplate">
<Setter.Value>
<DataTemplate>
Expand Down
44 changes: 44 additions & 0 deletions src/Toolkit/Toolkit/UI/Controls/FeatureForm/FeatureFormView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ public ScrollBarVisibility VerticalScrollBarVisibility
uint min = 0;
if (element.Input is TextAreaFormInput area) { max = area.MaxLength; min = area.MinLength; }
else if (element.Input is TextBoxFormInput tb) { max = tb.MaxLength; min = tb.MinLength; }
else if (element.Input is BarcodeScannerFormInput bar) { max = bar.MaxLength; min = bar.MinLength; }
if (max > 0 && min > 0)
return string.Format(Properties.Resources.GetString("FeatureFormOutsideLengthRange")!, min, max);
if (max > 0)
Expand Down Expand Up @@ -357,6 +358,28 @@ internal bool OnFormAttachmentClicked(FormAttachment attachment)
}
return false;
}

/// <summary>
/// Raised when the Barcode Icon in a barcode element is clicked.
/// </summary>
/// <remarks>
/// <para>Set the <see cref="BarcodeButtonClickedEventArgs.Handled"/> property to <c>true</c> to prevent
/// any default code and perform your own logic.</para>
/// </remarks>
public event EventHandler<BarcodeButtonClickedEventArgs>? BarcodeButtonClicked;

internal bool OnBarcodeButtonClicked(FieldFormElement element)
{
var handler = BarcodeButtonClicked;
if (handler is not null)
{
var args = new BarcodeButtonClickedEventArgs(element);

handler.Invoke(this, args);
return args.Handled;
}
return false;
}
}

/// <summary>
Expand All @@ -379,5 +402,26 @@ internal FormAttachmentClickedEventArgs(FormAttachment attachment)
/// </summary>
public FormAttachment Attachment { get; }
}

/// <summary>
/// Event argument for the <see cref="FeatureFormView.BarcodeButtonClicked"/> event.
/// </summary>
public class BarcodeButtonClickedEventArgs : EventArgs
{
internal BarcodeButtonClickedEventArgs(FieldFormElement element)
{
FormElement = element;
}

/// <summary>
/// Gets or sets a value indicating whether the event handler has handled the event and the default action should be prevented.
/// </summary>
public bool Handled { get; set; }

/// <summary>
/// Gets the element that was clicked.
/// </summary>
public FieldFormElement FormElement { get; }
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public partial class FieldFormElementView : TemplatedView
private static readonly DataTemplate DefaultRadioButtonsFormInputTemplate;
private static readonly DataTemplate DefaultTextAreaFormInputTemplate;
private static readonly DataTemplate DefaultTextBoxFormInputTemplate;
private static readonly DataTemplate DefaultBarcodeScannerFormInputTemplate;


static FieldFormElementView()
Expand All @@ -43,6 +44,7 @@ static FieldFormElementView()
DefaultRadioButtonsFormInputTemplate = new DataTemplate(BuildDefaultRadioButtonsFormInputTemplate);
DefaultTextAreaFormInputTemplate = new DataTemplate(BuildDefaultTextAreaFormInputTemplate);
DefaultTextBoxFormInputTemplate = new DataTemplate(BuildDefaultTextBoxFormInputTemplate);
DefaultBarcodeScannerFormInputTemplate = new DataTemplate(BuildDefaultBarcodeScannerFormInputTemplate);
}

private static object BuildDefaultComboBoxFormInputTemplate()
Expand Down Expand Up @@ -87,6 +89,13 @@ private static object BuildDefaultTextBoxFormInputTemplate()
return input;
}

private static object BuildDefaultBarcodeScannerFormInputTemplate()
{
var input = new TextFormInputView();
input.SetBinding(TextFormInputView.ElementProperty, Binding.SelfPath);
return input;
}

[DynamicDependency(nameof(Esri.ArcGISRuntime.Mapping.FeatureForms.FormElement.Label), "Esri.ArcGISRuntime.Mapping.FeatureForms.FormElement", "Esri.ArcGISRuntime")]
[DynamicDependency(nameof(Esri.ArcGISRuntime.Mapping.FeatureForms.FormElement.Description), "Esri.ArcGISRuntime.Mapping.FeatureForms.FormElement", "Esri.ArcGISRuntime")]
private static object BuildDefaultTemplate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public FieldFormElementView()
RadioButtonsFormInputTemplate = DefaultRadioButtonsFormInputTemplate;
TextAreaFormInputTemplate = DefaultTextAreaFormInputTemplate;
TextBoxFormInputTemplate = DefaultTextBoxFormInputTemplate;
BarcodeScannerFormInputTemplate = DefaultBarcodeScannerFormInputTemplate;
#else
DefaultStyleKey = typeof(FieldFormElementView);
#endif
Expand Down Expand Up @@ -148,7 +149,8 @@ Element.Input is SwitchFormInput ||
Element.Input is DateTimePickerFormInput ||
Element.Input is RadioButtonsFormInput ||
Element.Input is TextAreaFormInput ||
Element.Input is TextBoxFormInput);
Element.Input is TextBoxFormInput ||
Element.Input is BarcodeScannerFormInput);
#if MAUI
this.IsVisible = isVisible;
#else
Expand Down Expand Up @@ -291,6 +293,25 @@ public DataTemplate? TextBoxFormInputTemplate
#else
DependencyProperty.Register(nameof(TextBoxFormInputTemplate), typeof(DataTemplate), typeof(FieldFormElementView), new PropertyMetadata(null));
#endif

/// <summary>
/// Gets or sets the template for the <see cref="BarcodeScannerFormInputTemplate"/> element.
/// </summary>
public DataTemplate? BarcodeScannerFormInputTemplate
{
get { return (DataTemplate)GetValue(BarcodeScannerFormInputTemplateProperty); }
set { SetValue(BarcodeScannerFormInputTemplateProperty, value); }
}

/// <summary>
/// Identifies the <see cref="BarcodeScannerFormInputTemplate"/> dependency property.
/// </summary>
public static readonly DependencyProperty BarcodeScannerFormInputTemplateProperty =
#if MAUI
BindableProperty.Create(nameof(BarcodeScannerFormInputTemplate), typeof(DataTemplate), typeof(FieldFormElementView), null);
#else
DependencyProperty.Register(nameof(BarcodeScannerFormInputTemplate), typeof(DataTemplate), typeof(FieldFormElementView), new PropertyMetadata(null));
#endif
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,19 @@ public FieldTemplateSelector(FieldFormElementView parent)
RadioButtonsFormInput => _parent.RadioButtonsFormInputTemplate,
TextAreaFormInput => _parent.TextAreaFormInputTemplate,
TextBoxFormInput => _parent.TextBoxFormInputTemplate,
BarcodeScannerFormInput => _parent.BarcodeScannerFormInputTemplate,
#if MAUI
_ => null
#else
_ => base.SelectTemplate(item, container)
#endif
};
}
#if MAUI
return null;
#else
return base.SelectTemplate(item, container);
#endif
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Esri.ArcGISRuntime.Mapping.FeatureForms;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;

namespace Esri.ArcGISRuntime.Toolkit.Maui.Primitives
{
Expand Down Expand Up @@ -30,12 +31,14 @@ private class StringLengthConverter : IValueConverter
[DynamicDependency(nameof(Esri.ArcGISRuntime.Mapping.FeatureForms.FieldFormElement.Value), "Esri.ArcGISRuntime.Mapping.FeatureForms.FieldFormElement", "Esri.ArcGISRuntime")]
[DynamicDependency(nameof(Esri.ArcGISRuntime.Mapping.FeatureForms.TextAreaFormInput.MaxLength), "Esri.ArcGISRuntime.Mapping.FeatureForms.TextAreaFormInput", "Esri.ArcGISRuntime")]
[DynamicDependency(nameof(Esri.ArcGISRuntime.Mapping.FeatureForms.TextBoxFormInput.MaxLength), "Esri.ArcGISRuntime.Mapping.FeatureForms.TextBoxFormInput", "Esri.ArcGISRuntime")]
[DynamicDependency(nameof(Esri.ArcGISRuntime.Mapping.FeatureForms.BarcodeScannerFormInput.MaxLength), "Esri.ArcGISRuntime.Mapping.FeatureForms.BarcodeScannerFormInput", "Esri.ArcGISRuntime")]
private static object BuildDefaultTemplate()
{

Grid root = new Grid();
root.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Star));
root.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto));
root.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto));
HorizontalStackLayout horizontalStackLayout = new HorizontalStackLayout();
horizontalStackLayout.Margin = new Thickness(0, -17, 0, 0);
horizontalStackLayout.SetBinding(View.IsVisibleProperty, new Binding(nameof(TextFormInputView.ShowCharacterCount), source: RelativeBindingSource.TemplatedParent));
Expand All @@ -47,6 +50,7 @@ private static object BuildDefaultTemplate()
maxCountLabel.SetBinding(Label.TextProperty, new Binding("Element.Input.MaxLength", source: RelativeBindingSource.TemplatedParent));
horizontalStackLayout.Children.Add(maxCountLabel);
Grid.SetColumn(horizontalStackLayout, 1);
Grid.SetColumnSpan(horizontalStackLayout, 2);
root.Add(horizontalStackLayout);
Entry textInput = new Entry();
Grid.SetColumnSpan(textInput, 2);
Expand All @@ -63,13 +67,18 @@ private static object BuildDefaultTemplate()
Border errorBorder = new Border() { StrokeThickness = 1, Stroke = new SolidColorBrush(Colors.Red), IsVisible = false };
Grid.SetColumnSpan(errorBorder, 2);
root.Add(errorBorder);
Internal.CalciteImageButton barcodeButton = new Internal.CalciteImageButton("\uE22F") { IsVisible = false, BorderWidth = 0 };
Grid.SetColumn(barcodeButton, 2);
barcodeButton.SetBinding(View.IsVisibleProperty, new Binding(nameof(TextFormInputView.ShowBarcodeScanner), source: RelativeBindingSource.TemplatedParent));
root.Add(barcodeButton);

INameScope nameScope = new NameScope();
NameScope.SetNameScope(root, nameScope);
nameScope.RegisterName("ErrorBorder", errorBorder);
nameScope.RegisterName("TextInput", textInput);
nameScope.RegisterName("TextAreaInput", textArea);
nameScope.RegisterName("ReadOnlyText", readonlyText);
nameScope.RegisterName("BarcodeButton", barcodeButton);
return root;
}

Expand Down Expand Up @@ -100,9 +109,23 @@ protected override void OnApplyTemplate()
_textAreaInput.TextChanged += TextInput_TextChanged;
}
_readonlyLabel = GetTemplateChild("ReadOnlyText") as Label;
if (GetTemplateChild("BarcodeButton") is ImageButton imageButton)
{
imageButton.Clicked += BarcodeButton_Clicked;
}
else if (GetTemplateChild("BarcodeButton") is Button button)
{
button.Clicked += BarcodeButton_Clicked;
}
ConfigureTextBox();
UpdateValidationState();
}

private void BarcodeButton_Clicked(object? sender, EventArgs e)
{
if (Element != null)
FeatureFormView.GetFeatureFormViewParent(this)?.OnBarcodeButtonClicked(Element);
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
using System.Text;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using Esri.ArcGISRuntime.Toolkit.UI.Controls;

namespace Esri.ArcGISRuntime.Toolkit.Primitives
{
public partial class TextFormInputView : Control
{
private TextBox? _textInput;
private TextBlock? _readonlyLabel;
private ButtonBase? _barcodeButton;


/// <inheritdoc />
Expand All @@ -34,9 +36,24 @@ public override void OnApplyTemplate()
_textInput.TextChanged += TextInput_TextChanged;
}
_readonlyLabel = GetTemplateChild("ReadOnlyText") as TextBlock;
if (_barcodeButton != null)
{
_barcodeButton.Click -= BarcodeButton_Click;
}
_barcodeButton = GetTemplateChild("BarcodeButton") as ButtonBase;
if (_barcodeButton != null)
{
_barcodeButton.Click += BarcodeButton_Click;
}
ConfigureTextBox();
UpdateValidationState();
}

private void BarcodeButton_Click(object sender, RoutedEventArgs e)
{
if (Element != null)
FeatureFormView.GetFeatureFormViewParent(this)?.OnBarcodeButtonClicked(Element);
}
}
}
#endif
Loading

0 comments on commit ac1bc3e

Please sign in to comment.