I want to implement auto-complete on a textbox in a Windows 8 UI / Metro UI app using C#/XAML.
At the moment, when the soft / touch keyboard shows, it obscures the auto-complete box. However, on the text box focus, Windows 8 automatically scrolls the entire view up and ensures the text box is in focus.
In reality, all I want is the view to scroll up a little more (in fact, by the height of the auto-complete box).
I realise I can intercept the Showing event of InputPane.GetForCurrentView()
I can set InputPaneVisibilityEventArgs.EnsuredFocusedElementInView to true inside the Showing event fine (so Windows won't try to do anything).... however, how can I invoke the same scrolling functionality that Windows 8 would do, but ask it to scroll a little more!?
Here's the code for the main page:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,200,0,0">
<TextBlock HorizontalAlignment="Center" FontSize="60">App 1</TextBlock>
<TextBlock HorizontalAlignment="Center">Enter text below</TextBlock>
<TextBox HorizontalAlignment="Center" Margin="-10,0,10,0" Width="400" Height="30"/>
<ListBox HorizontalAlignment="Center" Width="400">
<ListBoxItem>Auto complete item 1</ListBoxItem>
<ListBoxItem>Auto complete item 2</ListBoxItem>
<ListBoxItem>Auto complete item 3</ListBoxItem>
<ListBoxItem>Auto complete item 4</ListBoxItem>
<ListBoxItem>Auto complete item 5</ListBoxItem>
</ListBox>
</StackPanel>
</Grid>
If you start up the simulator with the lowest resolution, use the hand to "touch" the textbox, this will bring up the soft keyboard. In the real app, the auto complete list will appear with items as the user enters text.
So in a nutshell, how can I move the screen up a bit more so the user can see the entire autocomplete list?
Bear in mind, in the real app, it'll be worse, as the user may not even notice the autocomplete list appearing "underneath" the keyboard.
I really would appreciate some advice, many thanks!
I have created an AutoCompleteBox for Windows Store apps, the nuget package is available at https://nuget.org/packages/AutoCompleteBoxWinRT
Ok, here is how I would tackle this since I cannot seem to find any way to control the scrolling of the app based on the appearance of the keyboard. I would create a user control that would form the basis for the auto-complete textbox.
<UserControl
x:Class="App6.MyUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App6"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<TextBox x:Name="textBox" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" GotFocus="textBox_GotFocus" LostFocus="textBox_LostFocus" />
<ListBox x:Name="listBox" Height="150" Margin="0,-150,0,0" VerticalAlignment="Top" Visibility="Collapsed"/>
</Grid>
This is an incredibly basic implementation, so you will have to tweak to meet your needs.
Then, I would add the following code-behind to the user control
public sealed partial class MyUserControl1 : UserControl
{
// Rect occludedRect;
bool hasFocus = false;
public MyUserControl1()
{
this.InitializeComponent();
InputPane.GetForCurrentView().Showing += MyUserControl1_Showing;
}
void MyUserControl1_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
{
if (hasFocus)
{
var occludedRect = args.OccludedRect;
var element = textBox.TransformToVisual(null);
var point = element.TransformPoint(new Point(0, 0));
if (occludedRect.Top < point.Y + textBox.ActualHeight + listBox.ActualHeight)
{
listBox.Margin = new Thickness(0, -listBox.ActualHeight, 0, 0); // Draw above
}
else
{
listBox.Margin = new Thickness(0, textBox.ActualHeight, 0, 0); // draw below
}
}
}
private void textBox_GotFocus(object sender, RoutedEventArgs e)
{
listBox.Visibility = Windows.UI.Xaml.Visibility.Visible;
hasFocus = true;
}
private void textBox_LostFocus(object sender, RoutedEventArgs e)
{
listBox.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
hasFocus = false;
}
}
Next steps would be to expose properties to pass data to be bound to the ListBox. Hard core would be ListBoxItem templating and more, depending on how reusable you wanted it to be.
Related
Getting acquainted with UWP. I'm developing an App for simulating electric circuits. There is a classic visual control called Frame, later called GroupBox in WPF.
It seems this control is absent in UWP.
There is a control called HeaderedContentControl in UWP.Toolkit library, but doesn't look the same. And seems the background and border properties don't work..
currently my code is:
<controls:HeaderedContentControl Margin="5"
BorderBrush="Black" BorderThickness="1"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<controls:HeaderedContentControl.Header>
<Border BorderBrush="Black" BorderThickness="1">
<Border.RenderTransform>
<TranslateTransform Y="-10"/>
</Border.RenderTransform>
<TextBlock Text="Resistor Value"/>
</Border>
</controls:HeaderedContentControl.Header>
<local:ComponentValueBox Unit="Ohm" HorizontalAlignment="Left"
Value="{x:Bind resistorValue, Mode=TwoWay}"
ValueChanged="changeR"/>
</controls:HeaderedContentControl>
And what I see (in the flyout) is:
Not quite like the GroupBox control..
What I would like to see is something like following:
What Should I do?
UWP is different from WPF. UWP is based on windows runtime, WPF is based on .NET Framework. They all use XAML to layout UI elments, but they have different XAML rendering engine. You could not think that MS dropped the old classic control. They're totally on the different platform. We call 'UWP' as Unversal Windows Platform. For now, you're not able to find such a 'GroupBox', but it's a new platform, you might be able to see such a control in the future. Anything is possible.
For your requirement, like #Muzib said, you entirely could make a custom control to meet your requirement. I used UserControl TextBlock Border ContentControl to make such a 'GroupBox' for your reference.
Please see my following code sample:
<UserControl
x:Class="AppGroupBox.GroupBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppGroupBox"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<TextBlock x:Name="HeaderTitle" Text="Header" Margin="7 0 0 0" LayoutUpdated="HeaderTitle_LayoutUpdated"></TextBlock>
<Border BorderBrush="Black" x:Name="border" BorderThickness="0 2 0 0" Margin="100 10 3 3" CornerRadius="0 5 0 0"></Border>
<Border BorderBrush="Black" BorderThickness="2 0 2 2" Margin="3 10 3 3" CornerRadius="5">
<ContentControl x:Name="Content" Margin="10 10 10 10">
</ContentControl>
</Border>
</Grid>
public sealed partial class GroupBox : UserControl
{
public GroupBox()
{
this.InitializeComponent();
}
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
// Using a DependencyProperty as the backing store for Header. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(GroupBox), new PropertyMetadata("Your Header", HeaderPropertyChangedCallback));
public static void HeaderPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
(d as GroupBox).HeaderTitle.Text = e.NewValue?.ToString();
//(d as GroupBox).border.Margin = new Thickness((d as GroupBox).HeaderTitle.ActualWidth, 10, 3, 3);
}
}
public object CustomContent
{
get { return (object)GetValue(CustomContentProperty); }
set { SetValue(CustomContentProperty, value); }
}
// Using a DependencyProperty as the backing store for Content. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CustomContentProperty =
DependencyProperty.Register("CustomContent", typeof(object), typeof(GroupBox), new PropertyMetadata(null,PropertyChangedCallback));
public static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
(d as GroupBox).Content.Content = e.NewValue;
}
}
private void HeaderTitle_LayoutUpdated(object sender, object e)
{
border.Margin = new Thickness(HeaderTitle.ActualWidth+10,10,3,3);
}
}
<local:GroupBox Header="My GroupBox" Height="300" Width="500">
<local:GroupBox.CustomContent>
<StackPanel>
<RadioButton Content="r1"></RadioButton>
<TextBox></TextBox>
</StackPanel>
</local:GroupBox.CustomContent>
</local:GroupBox>
I don't think there's such controls in UWP. Most probably you have to make your own CustomControl to achieve something that looks exactly lik that in UWP.
But hey, you can achieve something like that with a 'customized' ListView. Look at this:
<ListView Header="I am a header" BorderThickness="1" BorderBrush="Red" Width="250" Height="200" SelectionMode="None">
<ListView.HeaderTemplate>
<DataTemplate>
<ListViewHeaderItem Content="{Binding}"/>
</DataTemplate>
</ListView.HeaderTemplate>
<RadioButton>Any Value</RadioButton>
<RadioButton>1% standard?</RadioButton>
<RadioButton>5% standard</RadioButton>
</ListView>
It produces this output:
Of course You can make these items more dense if you want so.
I have a Popup which will fill the whole page when opened.
<Grid x:Name="gridRoot" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Open" HorizontalAlignment="Center" Click="{x:Bind viewModel.OpenPopup}" />
<Popup x:Name="popupCorrect" VerticalAlignment="Top" IsOpen="{Binding IsOpen}" IsLightDismissEnabled="False">
<Popup.ChildTransitions>
<TransitionCollection>
<PaneThemeTransition Edge="Left" />
</TransitionCollection>
</Popup.ChildTransitions>
<uc:MyPopup Width="{Binding ElementName=gridRoot, Path=ActualWidth}" Height="{Binding ElementName=gridRoot, Path=ActualHeight}"/>
</Popup>
</Grid>
The Popup is a UserControl
<Grid Background="Red">
<Button Content="Close" HorizontalAlignment="Center" Click="{x:Bind viewModel.ClosePopup}" />
</Grid>
The page
When popup is shown
Close the popup, resize the page, then reopen the popup. Notice that it does not match the new size of container page even though its Width and Height is bound to gridRoot . Do I have to manually set a new Width and Height for the popup? Why can't I achieve this with binding? This issue also appears on mobile during 'OrientationChanged'
Based on Decade Moon comment, this is how to resize the popup to match the parent container as its size changed.
Create a dependency property in the code behind
public double PageWidth
{
get { return (double)GetValue(PageWidthProperty); }
set { SetValue(PageWidthProperty, value); }
}
public static readonly DependencyProperty PageWidthProperty =
DependencyProperty.Register("PageWidth", typeof(double), typeof(GamePage), new PropertyMetadata(0d));
public double PageHeight
{
get { return (double)GetValue(PageHeightProperty); }
set { SetValue(PageHeightProperty, value); }
}
public static readonly DependencyProperty PageHeightProperty =
DependencyProperty.Register("PageHeight", typeof(double), typeof(GamePage), new PropertyMetadata(0d));
Update the value on SizeChanged event
private void GamePage_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize.Width > 0d && e.NewSize.Height > 0d)
{
PageWidth = e.NewSize.Width;
PageHeight = e.NewSize.Height;
}
}
Then in XAML, just use x:Bind to bind the popup width and height
<Popup x:Name="popupCorrect" VerticalAlignment="Top" IsOpen="{Binding IsPopupCorrectOpen, Mode=TwoWay}" IsLightDismissEnabled="False">
<Popup.ChildTransitions>
<TransitionCollection>
<PaneThemeTransition Edge="Left" />
</TransitionCollection>
</Popup.ChildTransitions>
<uc:PopupCorrect Width="{x:Bind PageWidth, Mode=TwoWay}" Height="{x:Bind PageHeight, Mode=TwoWay}"/>
</Popup>
Pretty straight forward. Just remember not to use the ActualWidth or ActualHeight properties for binding source as they do not raise the PropertyChanged event.
Although it has an ActualWidthProperty backing field, ActualWidth does not raise property change notifications and it should be thought of as a regular CLR property and not a dependency property.
For purposes of ElementName binding, ActualWidth does not post updates when it changes (due to its asynchronous and run-time calculated nature). Do not attempt to use ActualWidth as a binding source for an ElementName binding. If you have a scenario that requires updates based on ActualWidth, use a SizeChanged handler.
#PutraKg have a great way.
But I have two way to solve it.
The first is set the VerticalAlignment="Center" HorizontalAlignment="Center" that can make the popup in the center.
But I think youare not content to put it in the center.
The great way is use the screen position.
You can get the Grid's screen postion and make it to popup.
In open button
private void Button_OnClick(object sender, RoutedEventArgs e)
{
var grid = (UIElement)popupCorrect.Parent; //get grid
var p = grid.TransformToVisual (Window.Current.Content).TransformPoint(new Point(0, 0)); //get point
popupCorrect.HorizontalOffset = p.X;
popupCorrect.VerticalOffset = p.Y;
popupCorrect.IsOpen = !popupCorrect.IsOpen;
}
I am trying to change the input scope for a textbox in Windows Phone 8.1 app programmatically at runtime, but the change only works the first time.
I have this xaml page:
<Page
x:Class="InputScopeTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:InputScopeTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Margin="53,117,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="302"/>
<Button x:Name="buttonAlpha" Content="Alpha" HorizontalAlignment="Left" Margin="53,229,0,0" VerticalAlignment="Top" Click="buttonAlpha_Click"/>
<Button x:Name="buttonNumeric" Content="Numeric" HorizontalAlignment="Left" Margin="246,229,0,0" VerticalAlignment="Top" Click="buttonNumeric_Click"/>
</Grid>
</Page>
And this .cs page:
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
namespace InputScopeTest
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private void buttonAlpha_Click(object sender, RoutedEventArgs e)
{
InputScope scope = new InputScope();
InputScopeName name = new InputScopeName();
name.NameValue = InputScopeNameValue.AlphanumericFullWidth;
scope.Names.Add(name);
textBox.InputScope = scope;
}
private void buttonNumeric_Click(object sender, RoutedEventArgs e)
{
InputScope scope = new InputScope();
InputScopeName name = new InputScopeName();
name.NameValue = InputScopeNameValue.Number;
scope.Names.Add(name);
textBox.InputScope = scope;
}
}
}
On both the emulator and a windows phone 8.1 device the input scope for the textbox changes correctly only the first time you tap one of the buttons. For example if I tap the "Numeric" button first, the input scope changes to a numeric keyboard correctly. But if I then tap the "Alpha" button, the input scope does not change to an alphanumeric keyboard as it should.
The above code is taken from MSDN and it looks that it only works if you change the scope the first time. The second time the change is ignored.
Am I doing something wrong? Is there another way to set the input scope for a textbox programmatically multiple times?
So basically I want to have a button with a certain background image.
For example, when the app is loaded you would see a button with it's background image as image1.png and then when it is clicked you see image2.png as the background image. Then when you click again, the background image is switched back to image1.png.
Even though I have done this in C#, I want to do it in XAML because every time you click a button it automatically lights up according to the theme color, and the only way to get rid of that is via XAML.
Here is my code so fa:
<Button x:Name="Buttons" Content="" HorizontalAlignment="Left" Margin="155,0,0,69" BorderBrush="Transparent" Width="140" Click="Button_Click" Height="141" VerticalAlignment="Bottom">
<Button.Background>
<ImageBrush Stretch="Fill" ImageSource="/Assets/image1.png"/>
</Button.Background>
</Button>
Thanks in advance!
Try this,
http://visualstudiomagazine.com/articles/2013/02/15/customize-windows-phone-togglebutton.aspx
Here, the ToggleButton that ships with the SDK has been templated to add a clicked and unclicked image.
Alternate Solution with a checkbox:
Creating own toggle button in WP8?
VisualStudio 2017 "Blank App"
XAML
<Button x:Name="button" Content="Button1" HorizontalAlignment="Left" Margin="400,20,0,0" VerticalAlignment="Top" RenderTransformOrigin="-1.258,-5" Click="Button_Click" Height="80" Width="80"/>
C# (Set the original image in the properties of the button: right-click -> Brush -> image)
private void Button1_Click(object sender, RoutedEventArgs e)
{
button1.Background = new ImageBrush { ImageSource = new BitmapImage(new Uri("ms-appx:/Images/timerg.png", UriKind.RelativeOrAbsolute)) };
}
or C#
private void Button1_Click(object sender, RoutedEventArgs e)
{
BitmapImage bmp = new BitmapImage();
Uri u = new Uri("ms-appx:/Images/timer.png", UriKind.RelativeOrAbsolute);
bmp.UriSource = u;
// NOTE: change starts here
Image i = new Image();
i.Source = bmp;
button1.Content = i;
}
I'm building a Windows Phone 8.1 Hub Application. One of the hub section contains a ListView that displays a list of articles. I'd like to add a Textblock to this hubsection which displays a message when the articles failed to download. The XAML Code is below:
<HubSection
x:Uid="ArticlesSection"
Header="ARTICLES"
DataContext="{Binding Articles}"
HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}">
<DataTemplate>
<Grid>
<ListView
AutomationProperties.AutomationId="ItemListViewSection3"
AutomationProperties.Name="Items In Group"
SelectionMode="None"
IsItemClickEnabled="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource BannerBackgroundArticleTemplate}"
ItemClick="ItemView_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
</ListView>
<TextBlock
x:Name="NoArticlesTextBlock"
HorizontalAlignment="Center"
VerticalAlignment="center"
Style="{StaticResource HeaderTextBlockStyle}"
TextWrapping="WrapWholeWords"
TextAlignment="Center"/>
</Grid>
</DataTemplate>
</HubSection>
The problem I'm having is that I can't access the TextBlock from the C# code. Is there an easier way to do this?
The problem I'm having is that I can't access the TextBlock from the C# code.
Yes, since the TextBlock is defined inside a DataTemplate, the TextBlock won't be available until the DataTemplate has been applied. Thus, the x:Name attribute won't automatically generate a variable reference in the InitializeComponent method in your *.g.i.cs file. (Read up on XAML Namescopes for more information).
If you want to access it from your code-behind, there are two ways:
The first way is the simplest: you can get a reference to the TextBlock in the sender argument of the Loaded event handler for that TextBlock.
<TextBlock Loaded="NoArticlesTextBlock_Loaded" />
Then in your code-behind:
private TextBlock NoArticlesTextBlock;
private void NoArticlesTextBlock_Loaded(object sender, RoutedEventArgs e)
{
NoArticlesTextBlock = (TextBlock)sender;
}
The second way is to traverse the visual tree manually to locate the element with the required name. This is more suitable for dynamic layouts, or when you have a lot of controls you want to reference that doing the previous way would be too messy. You can achieve it like this:
<Page Loaded="Page_Loaded" ... />
Then in your code-behind:
static DependencyObject FindChildByName(DependencyObject from, string name)
{
int count = VisualTreeHelper.GetChildrenCount(from);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(from, i);
if (child is FrameworkElement && ((FrameworkElement)child).Name == name)
return child;
var result = FindChildByName(child, name);
if (result != null)
return result;
}
return null;
}
private TextBlock NoArticlesTextBlock;
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Note: No need to start searching from the root (this), we can just start
// from the relevant HubSection or whatever. Make sure your TextBlock has
// x:Name="NoArticlesTextBlock" attribute in the XAML.
NoArticlesTextBlock = (TextBlock)FindChildByName(this, "NoArticlesTextBlock");
}
Jerry Nixon has a good page on his blog about this.