Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/xamarin/Xamarin.PropertyEditing.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Maupin <ermaup@microsoft.com>2018-08-31 21:01:06 +0300
committerEric Maupin <ermaup@microsoft.com>2018-09-05 00:02:35 +0300
commitc9b474b066ee6b9e8e74a8fbcb5b625a113b627c (patch)
tree47b6e56635c4f710952aa2bea773bc640a4dfa8d /Xamarin.PropertyEditing.Windows
parent718623c0ef4257404e44673676c8fa69f30c9000 (diff)
[Win] Add windows basic autocomplete support
Diffstat (limited to 'Xamarin.PropertyEditing.Windows')
-rw-r--r--Xamarin.PropertyEditing.Windows/EntryPopup.cs46
-rw-r--r--Xamarin.PropertyEditing.Windows/TextBoxEx.cs144
-rw-r--r--Xamarin.PropertyEditing.Windows/Themes/Resources.xaml38
3 files changed, 192 insertions, 36 deletions
diff --git a/Xamarin.PropertyEditing.Windows/EntryPopup.cs b/Xamarin.PropertyEditing.Windows/EntryPopup.cs
index fbc3a05..08db76b 100644
--- a/Xamarin.PropertyEditing.Windows/EntryPopup.cs
+++ b/Xamarin.PropertyEditing.Windows/EntryPopup.cs
@@ -1,6 +1,5 @@
using System;
using System.Windows;
-using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
@@ -9,6 +8,20 @@ namespace Xamarin.PropertyEditing.Windows
internal class EntryPopup
: Popup
{
+ static EntryPopup ()
+ {
+ // Autocomplete is a popup in a popup and when you go to click the other popup, this one might close
+ // so we need to hack around this otherwise uncontrollable behavior (StaysOpen has no effect).
+ var existing = IsOpenProperty.GetMetadata (typeof(Popup));
+ PropertyChangedCallback callback = (o,e) => {
+ if ((bool) e.NewValue || ((EntryPopup) o).CanClose ()) {
+ existing.PropertyChangedCallback (o, e);
+ }
+ };
+
+ IsOpenProperty.OverrideMetadata(typeof(EntryPopup), new FrameworkPropertyMetadata (existing.DefaultValue, callback, existing.CoerceValueCallback));
+ }
+
public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register (
nameof(ContentTemplate), typeof(DataTemplate), typeof(EntryPopup), new PropertyMetadata ((s,e) => ((EntryPopup)s).UpdateContentTemplate()));
@@ -18,6 +31,15 @@ namespace Xamarin.PropertyEditing.Windows
set { SetValue (ContentTemplateProperty, value); }
}
+ public static readonly DependencyProperty ValueProperty = DependencyProperty.Register (
+ "Value", typeof(string), typeof(EntryPopup), new PropertyMetadata (default(string)));
+
+ public string Value
+ {
+ get { return (string) GetValue (ValueProperty); }
+ set { SetValue (ValueProperty, value); }
+ }
+
public override void OnApplyTemplate ()
{
base.OnApplyTemplate ();
@@ -33,15 +55,20 @@ namespace Xamarin.PropertyEditing.Windows
protected override void OnClosed (EventArgs e)
{
if (!this.closingFromEscape) {
- this.textBox.GetBindingExpression (TextBox.TextProperty)?.UpdateSource();
+ GetBindingExpression (ValueProperty)?.UpdateSource ();
} else
this.closingFromEscape = false;
base.OnClosed (e);
}
- protected override void OnPreviewKeyDown (KeyEventArgs e)
+ protected override void OnKeyDown (KeyEventArgs e)
{
+ base.OnKeyDown (e);
+
+ if (e.Handled)
+ return;
+
if (e.Key == Key.Escape) {
this.closingFromEscape = true;
IsOpen = false;
@@ -50,22 +77,25 @@ namespace Xamarin.PropertyEditing.Windows
IsOpen = false;
e.Handled = true;
}
-
- base.OnPreviewKeyDown (e);
}
- private TextBox textBox;
+ private TextBoxEx textBox;
private bool closingFromEscape;
+ private bool CanClose ()
+ {
+ return this.textBox.CanCloseParent ();
+ }
+
private void UpdateContentTemplate()
{
Child = ContentTemplate?.LoadContent() as UIElement;
if (Child == null)
return;
- this.textBox = ((FrameworkElement)Child)?.FindName ("entry") as TextBox;
+ this.textBox = ((FrameworkElement)Child)?.FindName ("entry") as TextBoxEx;
if (this.textBox == null)
- throw new InvalidOperationException ("Need an entry TextBox for EntryPopup");
+ throw new InvalidOperationException ("Need an entry TextBoxEx for EntryPopup");
}
}
} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing.Windows/TextBoxEx.cs b/Xamarin.PropertyEditing.Windows/TextBoxEx.cs
index 295ea32..74863fe 100644
--- a/Xamarin.PropertyEditing.Windows/TextBoxEx.cs
+++ b/Xamarin.PropertyEditing.Windows/TextBoxEx.cs
@@ -1,19 +1,19 @@
using System;
+using System.Collections;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace Xamarin.PropertyEditing.Windows
{
[TemplatePart (Name ="PART_Clear", Type = typeof (Button))]
+ [TemplatePart (Name = "PART_CompletePopup", Type = typeof (Popup))]
+ [TemplatePart (Name = "PART_CompleteList", Type = typeof (ListBox))]
internal class TextBoxEx
: TextBox
{
- public TextBoxEx()
- {
- PreviewKeyDown += OnPreviewKeyDown;
- }
public static readonly DependencyProperty HintProperty = DependencyProperty.Register (
"Hint", typeof(object), typeof(TextBoxEx), new PropertyMetadata (default(object)));
@@ -42,6 +42,15 @@ namespace Xamarin.PropertyEditing.Windows
set { SetValue (FocusSelectsAllProperty, value); }
}
+ public static readonly DependencyProperty EnableClearProperty = DependencyProperty.Register (
+ "EnableClear", typeof(bool), typeof(TextBoxEx), new PropertyMetadata (true));
+
+ public bool EnableClear
+ {
+ get { return (bool) GetValue (EnableClearProperty); }
+ set { SetValue (EnableClearProperty, value); }
+ }
+
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register (
"ShowClearButton", typeof(bool), typeof(TextBoxEx), new PropertyMetadata (default(bool)));
@@ -60,6 +69,15 @@ namespace Xamarin.PropertyEditing.Windows
set { SetValue (ClearButtonStyleProperty, value); }
}
+ public static readonly DependencyProperty EnableSubmitProperty = DependencyProperty.Register (
+ "EnableSubmit", typeof(bool), typeof(TextBoxEx), new PropertyMetadata (true));
+
+ public bool EnableSubmit
+ {
+ get { return (bool) GetValue (EnableSubmitProperty); }
+ set { SetValue (EnableSubmitProperty, value); }
+ }
+
public static readonly DependencyProperty SubmitButtonStyleProperty = DependencyProperty.Register (
"SubmitButtonStyle", typeof(Style), typeof(TextBoxEx), new PropertyMetadata (default(Style)));
@@ -69,6 +87,24 @@ namespace Xamarin.PropertyEditing.Windows
set { SetValue (SubmitButtonStyleProperty, value); }
}
+ public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register (
+ "ItemsSource", typeof (IEnumerable), typeof (TextBoxEx), new PropertyMetadata (default (IEnumerable)));
+
+ public IEnumerable ItemsSource
+ {
+ get { return (IEnumerable)GetValue (ItemsSourceProperty); }
+ set { SetValue (ItemsSourceProperty, value); }
+ }
+
+ public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register (
+ "ItemTemplate", typeof (DataTemplate), typeof (TextBoxEx), new PropertyMetadata (default (DataTemplate)));
+
+ public DataTemplate ItemTemplate
+ {
+ get { return (DataTemplate)GetValue (ItemTemplateProperty); }
+ set { SetValue (ItemTemplateProperty, value); }
+ }
+
public override void OnApplyTemplate ()
{
base.OnApplyTemplate ();
@@ -81,6 +117,14 @@ namespace Xamarin.PropertyEditing.Windows
return;
}
+ this.popup = (Popup)GetTemplateChild ("PART_CompletePopup");
+ this.list = (ListBox)GetTemplateChild ("PART_CompleteList");
+ if (this.popup == null || this.list == null)
+ throw new InvalidOperationException ("PART_CompletePopup and PART_CompleteList must be present");
+
+ this.list.ItemContainerGenerator.ItemsChanged += OnItemsChanged;
+ this.list.PreviewMouseLeftButtonDown += OnListMouseDown;
+
clear.Click += (sender, e) => {
Clear();
};
@@ -114,8 +158,14 @@ namespace Xamarin.PropertyEditing.Windows
protected override void OnLostKeyboardFocus (KeyboardFocusChangedEventArgs e)
{
+ if (this.defocusFromList)
+ return;
+
base.OnLostKeyboardFocus (e);
- OnSubmit();
+ this.popup.IsOpen = false;
+
+ if (EnableSubmit)
+ OnSubmit ();
}
protected virtual void OnSubmit()
@@ -124,6 +174,64 @@ namespace Xamarin.PropertyEditing.Windows
expression?.UpdateSource ();
}
+ protected override void OnPreviewKeyDown (KeyEventArgs e)
+ {
+ e.Handled = true;
+ if (e.Key == Key.Down) {
+ if (this.list.SelectedIndex == -1 || this.list.SelectedIndex + 1 == this.list.Items.Count)
+ this.list.SelectedIndex = 0;
+ else
+ this.list.SelectedIndex++;
+
+ this.list.ScrollIntoView (this.list.SelectedItem);
+ } else if (e.Key == Key.Up) {
+ if (this.list.SelectedIndex == -1 || this.list.SelectedIndex == 0)
+ this.list.SelectedIndex = this.list.Items.Count - 1;
+ else
+ this.list.SelectedIndex--;
+
+ this.list.ScrollIntoView (this.list.SelectedItem);
+ } else if (e.Key == Key.Enter || e.Key == Key.Tab) {
+ if (this.list.SelectedValue != null) {
+ SelectCompleteItem (this.list.SelectedItem);
+ } else if (!this.popup.IsOpen) {
+ if (EnableSubmit)
+ OnSubmit ();
+ else
+ e.Handled = false;
+ }
+ } else if (e.Key == Key.Escape) {
+ if (this.popup.IsOpen)
+ this.popup.IsOpen = false;
+ else if (EnableClear)
+ Clear ();
+ else
+ e.Handled = false;
+ } else {
+ e.Handled = false;
+ }
+
+ base.OnPreviewKeyDown (e);
+ }
+
+ protected internal bool CanCloseParent ()
+ {
+ bool can = !this.defocusFromList;
+ this.defocusFromList = false;
+ return can;
+ }
+
+ private bool defocusFromList;
+ private Popup popup;
+ private ListBox list;
+
+ private void SelectCompleteItem (object item)
+ {
+ Text = item.ToString ();
+ CaretIndex = Text.Length;
+ this.popup.IsOpen = false;
+ }
+
private void FocusSelect()
{
if (!FocusSelectsAll)
@@ -133,13 +241,27 @@ namespace Xamarin.PropertyEditing.Windows
SelectAll ();
}
- private void OnPreviewKeyDown (object sender, System.Windows.Input.KeyEventArgs e)
+ private void OnItemsChanged (object sender, ItemsChangedEventArgs e)
{
- if (e.Key == Key.Enter) {
- OnSubmit();
- } else if (e.Key == Key.Escape) {
- Clear();
- }
+ if (!HasEffectiveKeyboardFocus)
+ return;
+
+ this.popup.IsOpen = (this.list.Items.Count > 0);
+ if (this.list.SelectedIndex == -1)
+ this.list.SelectedIndex = 0;
+ }
+
+ private void OnListMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ Point pos = e.GetPosition (this.list);
+ var element = this.list.InputHitTest (pos) as FrameworkElement;
+ var item = element?.FindParentOrSelf<ListBoxItem> ();
+ if (item == null)
+ return;
+
+ SelectCompleteItem (item.DataContext);
+ this.defocusFromList = true;
+ e.Handled = true;
}
}
}
diff --git a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
index 0772b60..75b5b92 100644
--- a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
+++ b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
@@ -497,7 +497,8 @@
<Style x:Key="CustomExpressionPopup" TargetType="local:EntryPopup">
<Setter Property="Placement" Value="Bottom" />
<Setter Property="Width" Value="400" />
- <Setter Property="StaysOpen" Value="False" />
+ <Setter Property="StaysOpen" Value="True" />
+ <Setter Property="Value" Value="{Binding CustomExpression,Mode=TwoWay,UpdateSourceTrigger=Explicit}" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
@@ -509,7 +510,7 @@
</Grid.RowDefinitions>
<TextBlock Name="label" Grid.Row="0" Margin="5,5,0,5" FontWeight="Bold" Text="{x:Static prop:Resources.CustomExpression}" />
- <local:TextBoxEx x:Name="entry" Grid.Row="1" Margin="5,0,5,5" AutomationProperties.LabeledBy="{Binding Mode=OneTime,ElementName=label}" FocusSelectsAll="True" Text="{Binding CustomExpression,Mode=TwoWay,UpdateSourceTrigger=Explicit}" />
+ <local:TextBoxEx x:Name="entry" Grid.Row="1" Margin="5,0,5,5" AutomationProperties.LabeledBy="{Binding Mode=OneTime,ElementName=label}" FocusSelectsAll="True" EnableClear="False" EnableSubmit="False" Text="{Binding PreviewCustomExpression,Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding AutocompleteItems}" />
</Grid>
</Border>
</DataTemplate>
@@ -1631,8 +1632,6 @@
<Style TargetType="ListBoxItem">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Padding" Value="4,1"/>
- <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
- <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Foreground" Value="{DynamicResource ListItemForegroundBrush}" />
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
@@ -1942,21 +1941,26 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TextBoxEx}">
- <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
- <Grid>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="*" />
- <ColumnDefinition Width="Auto" />
- </Grid.ColumnDefinitions>
+ <Grid>
+ <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
- <ContentPresenter Grid.Column="0" Name="hintContent" Margin="2,0,2,0" Visibility="Collapsed" ContentSource="Hint" />
- <ScrollViewer x:Name="PART_ContentHost" Grid.Column="0" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
+ <ContentPresenter Grid.Column="0" Name="hintContent" Margin="2,0,2,0" Visibility="Collapsed" ContentSource="Hint" />
+ <ScrollViewer x:Name="PART_ContentHost" Grid.Column="0" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
- <Button Name="PART_Clear" Grid.Column="1" Style="{TemplateBinding ClearButtonStyle}" Visibility="{TemplateBinding ShowClearButton,Converter={StaticResource BoolToVisibilityConverter}}" />
- <Button Name="PART_Submit" Grid.Column="1" Style="{TemplateBinding SubmitButtonStyle}" Visibility="Collapsed" />
- </Grid>
- </Border>
- <ControlTemplate.Triggers>
+ <Button Name="PART_Clear" Grid.Column="1" Style="{TemplateBinding ClearButtonStyle}" Visibility="{TemplateBinding ShowClearButton,Converter={StaticResource BoolToVisibilityConverter}}" />
+ <Button Name="PART_Submit" Grid.Column="1" Style="{TemplateBinding SubmitButtonStyle}" Visibility="Collapsed" />
+ </Grid>
+ </Border>
+ <Popup Name="PART_CompletePopup" Placement="Bottom" StaysOpen="True" Focusable="False" Width="{Binding ElementName=border,Path=ActualWidth}">
+ <ListBox Name="PART_CompleteList" Focusable="False" MaxHeight="200" ItemsSource="{TemplateBinding ItemsSource}" ItemTemplate="{TemplateBinding ItemTemplate}" />
+ </Popup>
+ </Grid>
+ <ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>