I'm creating an XML Form Reader. Each of the control types are User Controls. However when the user taps somewhere on the form that isn't a focused control (i.e. TextBlock), the form jumps focus to the first focusable control in the page. (i.e. TextBox)
This scrolls the page to the top (usually), taking the user away from what they were just working on.
I have tried a lot of different things around the focus events but have hit a wall on how to solve this.
What would be the best way to handle this ? Any hints or tips would be greatly appreciated.
Thanks
EDIT (more info)
The "MASTER" page has a StackPanel
<StackPanel x:Name="pnlFormMain" ScrollViewer.BringIntoViewOnFocusChange="False"/>
For each control in the xml form, a UserControl is added to the stackpanel.
<UserControl.Resources>
<Storyboard x:Name="Show">
<DoubleAnimation Duration="0:0:0.6" To="1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="_WidgetMaster" d:IsOptimized="True"/>
</Storyboard>
<Storyboard x:Name="Hide">
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="_WidgetMaster" d:IsOptimized="True"/>
</Storyboard>
</UserControl.Resources>
<Grid x:Name="grdInput" ScrollViewer.BringIntoViewOnFocusChange="False">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Grid.Row="0" ScrollViewer.BringIntoViewOnFocusChange="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<!-- label -->
<TextBlock x:Name="lblLabel" Text="Label" Grid.Column="0" VerticalAlignment="Center" Margin="3,6,5,5" TextWrapping="Wrap" />
<!-- HINT -->
<Ellipse x:Name="ellHint" Height="25" Width="25" Grid.Column="1" StrokeThickness="2" Stroke="LightBlue" Margin="5,0,5,0" Tapped="Hint_Tapped" VerticalAlignment="Center">
<Ellipse.Fill>
<ImageBrush ImageSource="ms-appx:///XForms/Assets/information.png" />
</Ellipse.Fill>
</Ellipse>
</Grid>
<Frame x:Name="control" Grid.Column="1" Grid.Row="0" />
<TextBlock x:Name="lblConstraintMessage" Grid.Row="2" Grid.ColumnSpan="99" FontSize="10" VerticalAlignment="Center"
HorizontalAlignment="Right" Margin="0,0,5,0" Visibility="Collapsed" />
</Grid>
The same UserControl is used for almost all controls. And the input for that control is a second UserControl that goes into the Frame near the bottom.
(e.g.)
<Grid x:Name="grdInput">
<TextBox x:Name="txtInput" Text="" Grid.Row="0" Grid.Column="1" Margin="15,0,0,0" LostFocus="txtInput_LostFocus" KeyDown="txtInput_KeyDown" TextAlignment="Right" />
<Border x:Name="brdrValuePlaceholder" Background="Transparent" BorderThickness="0,0,0,1" BorderBrush="{ThemeResource TextControlBorderBrush}" HorizontalAlignment="Stretch"
Height="{ThemeResource TextControlThemeMinHeight}" Visibility="Collapsed">
<TextBlock x:Name="lblInput" Text="" Grid.Row="0" Grid.Column="1" Margin="0,0,5,0" Tapped="lblInput_Tapped" TextAlignment="Right" HorizontalAlignment="Stretch"/>
</Border>
</Grid>
The LostFocus event there just sets a value in the database
Other controls on the master page is a Popup and a ProgressRing
Along with a header bar.
EDIT 2
I have tried to do what I can with making the StackPanel unfocusable, and storing the previously focused control, but can get them to work as hoped.
I have put the source up on GitHub, I am new to GitHub, so if I need to do anything more, please let me know. The code is a bit older, but has the same problem. There is also a Android and iOS application in the solution, but only the UWP project has the issue.
The bounty is still alive, and will be awarded if anyone can help.
Thanks
Maybe it is focusing on the stackpanel itself?
Have dialog box popup or a textstring in the page itself to show what is always focused. That way you can see if the stacklayout is becoming focused.
Another possibility is that if you tap off, it is returning focus to the first element on the stacklayout (what it saw as tapped).
It's not trivial to recreate your exact scenario. However, there should be several possible solutions to your problem.
The first thing that comes to my mind is to intercept the Preview*keyboard/mouse events within your form.
When a KeyDown/MouseDown/Tap outside your text box is detected while it has the focus, call update the binding expression of the FocusManager, and set Handled=true to prevent the KeyDown or MouseDown event from being processed further if a non-user control is clicked. A markup extension could be helpful too.
It might be also crucial to hangle PreviewLost*Focus to detect any unwanted focus changes.
Another approach could be to mark all no-user input UI elements as Focusable=false in the XAML, e.g.
<XY Focusable="False">
...
</XY>
or disable them all together.
Related
This is most certainly the most confusing thing I have encountered while programming as of yet. For some unknown reason only a small horizontal band of the XAML controls (in this case a Button (the one furthest up) and a TextBlock) is visible and if the application is not in fullscreen, nothing can be seen! On the other hand, the Button to the left (the one furthest down in the code), which is a pure copy of the one you get via the Visual Studio "Toolbox" under "Common XAML Controls", works fine!
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button x:Name="button" Content="My button name" Height="77" Margin="809,198,0,805" Width="353"/>
<TextBlock x:Name="textBlock" Height="83" Text="An example...." Margin="809,428,0,0" Width="353" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="button1" Content="Button" HorizontalAlignment="Left" Height="117" Margin="542,327,0,0" VerticalAlignment="Top" Width="156"/>
</Grid>
The file containing the three XAML controls is the only file that have been changed, otherwise it is simply the default Blank UWP in Visual Studio. I am using the Desktop (1920 x 1080) format.
In light of what I have written previously, could someone please tell me what I can do to fix this because I am utterly clueless.
Answer: After having fooled around with another project exhibiting similar problems I stumbled upon a solution. The problem is that for some reason or another the XAML controls do not work properly if the the margin on the opposite side is not 0 (top vs bottom, right vs left). In order to still be able to specify the position of the XAML control HorizontalAlignment and VerticalAlignment has to be added and the orientation be the same as the parameter you input into the Margin. Therefore, if you have specified the right Margin for it, you must also set the HorizontalAlignment to right as seen below. The Opposite can also be done.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button x:Name="button" Content="My button name" Height="77" Margin="0,0,500,900" Width="353" VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
<TextBlock x:Name="textBlock" Height="83" Text="An example...." Margin="809,428,0,0" Width="353" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="button1" Content="Button" HorizontalAlignment="Left" Height="117" Margin="542,327,0,0" VerticalAlignment="Top" Width="156"/>
</Grid>
So I'm not sure what you mean by a class to allow it. Some dependency properties of the controls used is all you need. So try this. Literally took less than a minute and ask if you have questions. There's a lot of different ways to do the same thing depending on your style/habits, this is just one.
output:
snippet:
<!-- We have a bit of a margin on the parent for spacing
and shrink the space to what's necessary. -->
<Grid Background="White" Margin="10,10,20,10"
VerticalAlignment="Top">
<Grid.RowDefinitions>
<!-- Let's let our odd # rows do our spacing instead of random margins -->
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="6*"/>
</Grid.ColumnDefinitions>
<Grid.Resources>
<!-- So we don't set the same properties on every instance. -->
<Style TargetType="TextBlock" BasedOn="{StaticResource BaseTextBlockStyle}">
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="5"/>
</Style>
</Grid.Resources>
<TextBlock Grid.ColumnSpan="2" Text="Exaaaaaaamples"
HorizontalAlignment="Center"/>
<TextBlock Grid.Row="2"
Text="Email:"/>
<TextBox Grid.Row="2" Grid.Column="1"/>
<TextBlock Grid.Row="4"
Text="Username:"/>
<TextBox Grid.Row="4" Grid.Column="1"/>
<TextBlock Grid.Row="6"
Text="Password:"/>
<PasswordBox Grid.Row="6" Grid.Column="1"/>
<TextBlock Grid.Row="8"
Text="Age of Account:"/>
<TextBox Grid.Row="8" Grid.Column="1"/>
</Grid>
Hope this is inline with what you're looking for, if not say so and we'll get you sorted. :)
I have created a usercontrol that has some controls outside of view, but they will be visible as soon as animation show them. Animation is changing their offset, so they slowly appears. Now, when I put my usercontrol on some view,
those controls that I don't want to be seen are visible. I tied to use Grid, GridView, Canvas...but everytime I can see these controls.
I need 4 of my usercontrols in a row, to be resizeble. And this issue makes some usercontrols to overlap eachother.
Here is how I have show them in grid:
<Grid x:Name="gridDown" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="25*" />
</Grid.ColumnDefinitions>
<local:MyUserControl x:Name="myControl1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0"/>
<local:MyUserControl x:Name="myControl2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="1"/>
<local:MyUserControl x:Name="myControl3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="2"/>
<local:MyUserControl x:Name="myControl4" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="3"/>
</Grid>
How to show only central part of my usercontrol? How to hide rest of the controls that I don't want to be seen? What control is the best for this?
To control what's displayed you need to tell it where to set the limits of what to show. In WPF has the helpful ClipToBounds property but unfortunately this isn't available in UWP so you need to set the 'Clip' property yourself.
So, if your page looked like this
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<local:MyControl/>
<local:MyControl/>
<local:MyControl/>
</StackPanel>
And your control XAML looked like this
<Grid Height="50" Width="100">
<Grid.Clip>
<RectangleGeometry Rect="0 0 100 50"/>
</Grid.Clip>
<Grid.Resources>
<Storyboard x:Name="ShowRedBit">
<DoubleAnimation Storyboard.TargetProperty="Rectangle.RenderTransform.(CompositeTransform.TranslateX)"
Storyboard.TargetName="RedBit"
Duration="0:0:2"
To="0"/>
</Storyboard>
</Grid.Resources>
<Button HorizontalAlignment="Center" Click="Button_Click">show red</Button>
<Rectangle Fill="Red" Height="50" Width="50" x:Name="RedBit" RenderTransformOrigin="0.5,0.5" >
<Rectangle.RenderTransform>
<CompositeTransform TranslateX="-100"/>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
You can see that I've manually set the clip property to be the same as the size of the control.
The answers to this question also show some other approaches, including a way to set it dynamically.
I am currently working on Windows Universal Apps.In that there is requirement to show menu from left side when User clicks on menu icon. I want add a ListView inside it and handle the selectionchanged event based on user's selected item. Now, the problem with Flyout is that it opens like a popup on clicking the icon but what I actually want to do is it should come from left side of the window .For e.g in Gmail application of android. Please can anyone suggest how to achieve this. Please find below my code which I added in Flyout below:
<Image Source="ms-appx:///Images/menu_image.png"
HorizontalAlignment="Left"
Tapped="Image_Tapped"
Width="60"
Height="90"
Grid.Column="0"
VerticalAlignment="Center">
<FlyoutBase.AttachedFlyout>
<Flyout>
<Grid x:Name="SettingsPane"
Background="{StaticResource AppBackGroundColor}"
Grid.Row="0"
Width="380">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EdgeUIThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Margin="8">
<TextBlock Name="SidebarTitletxtblk"
FontSize="25"
TextWrapping="Wrap"
Style="{StaticResource BaseTextBlockStyle}" />
</StackPanel>
<ListView Grid.Row="1"
x:Name="LocationPickerList"
SelectionChanged="LocationPickTypeSelected"
Margin="0,10,0,0"
ItemContainerStyle="{StaticResource GenericListViewContainerStyle}"
ItemTemplate="{StaticResource LocationPickerListItemTemplate}"></ListView>
</Grid>
</Flyout>
</FlyoutBase.AttachedFlyout>
</Image>
You can't override the Flyout's standard transition. If you want to apply something else then you can use a Popup instead and customize it however you'd like. To have it slide in from the left apply an EdgeUIThemeTransition (if it's short) or a PaneThemeTransition (if it's full height) with Edge=Left.
For example:
<Popup x:Name="flyoutPane" IsOpen="False" IsLightDismissEnabled="True"
Width="320" HorizontalAlignment="Left">
<Popup.ChildTransitions>
<TransitionCollection>
<!--<EdgeUIThemeTransition Edge="Left" />-->
<PaneThemeTransition Edge="Left" />
</TransitionCollection>
</Popup.ChildTransitions>
<Grid Width="380" Height="{Binding ElementName=flyoutPane, Path=Height}" Background="{ThemeResource FlyoutBackgroundThemeBrush}" >
<TextBlock Text="Grid contents here" />
</Grid>
</Popup>
And trigger it from your Button Click (your Image sounds like it should be a Button rather than using Tap, unless you have an alternate keyboard method - you can template off the button look while keeping the button semantics).
private void Button_Click(object sender, RoutedEventArgs e)
{
// Height is only important if we want the Popup sized to the screen
flyoutPane.Height = Window.Current.Bounds.Height;
flyoutPane.IsOpen = true;
}
If you're doing many of these you can create a custom control with an attached property similar to FlyoutBase.AttachedFlyout.
This doesn't actually describe what I mean, but I'll try to explain. I've been using C# for an year now and never touched WPF. Only recently I've realized how awesome it is and started using it. I'm now facing a problem.
I want to let the user know the password/username are incorrect, so instead of the old WinForms MessageBox I want to make it more pleasent. I thought about creating a grid that tints the application darker, and then I'll be able to show some text on it. However - how's that possible?... Do you have any nicer ideas to show a message for the application (not a popup)? Thanks.
You can create an UserControl with translucent background (like #33000000), and 3-row grid to show title, message and OK button, like bellow:
<UserControl x:Class="ApplicationNameSpace.MessageControl" ... >
<Grid Background="#33000000" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Background="#FFFFFF" MinHeight="100" MinWidth="200">
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="35"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#EEEEEE">
<Label Content="Unable to Login" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<Grid Grid.Row="1" Margin="10,20">
<TextBlock Text="Wrong username or password. Try again." HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
<Grid Grid.Row="2" Background="#EEEEEE">
<Button Content="OK" Width="80" Height="25" />
</Grid>
</Grid>
</Grid>
</UserControl>
To use, you can add the control at the end of your window, and change the visibility to Visible when needs to show it.
<Window x:Class="ApplicationNameSpace.MainWindow"
xmlns:local="clr-namespace:ApplicationNameSpace" ... >
<Grid>
...
<local:MessageControl Name="messageControl" Visibility="Collapsed" />
</Grid>
</Window>
You can also create a generic control that you pass to a show method the title and message content, like MessageBox show method. You can also add the user control element programmatically in the window in this method.
You can use the validation and the INotifyDataError which is on WPF 4.5 and you can show a nice message next to the textbox check this link for example
User control details:
Have created drop down list control (Like as combo box), clicking on down arrow button, it displays a list below the text box
I have set zIndex property of my User control
Issue:
Case 1: When there is another user control (other than my custom user control), and if drop down list is displayed, other user control hides behind my user control. This is perfectly Ok
Case 2: There are 2 Custom User controls, if list is displayed from first user control, second user control appears on the list. This is where i am facing issue
XAML of my control is as below
<UserControlx:Class="UserControls.AutoCompleteComboBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Panel.ZIndex="1110" LostFocus="UserControl_LostFocus" Height="Auto">
<Canvas Name="MainCanvas">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="150"></ColumnDefinition>
<ColumnDefinition Width="20"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Name="autoTextBox" Height="20" MinWidth="150" Width="Auto" MinHeight="20" Style="{DynamicResource AutoCompleteBox}" BorderThickness="2"
Margin="0,0,0,0" TextWrapping="NoWrap" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
<Button Content="6" FontFamily="Marlett" Grid.Row="0" Grid.Column="1" FontSize="15" Margin="0,0,0,0" Height="20" Width="20" HorizontalAlignment="Right" VerticalAlignment="Top" Background="{StaticResource BackgroudBlueBrush}" Click="Button_Click" Padding="0" Cursor="Hand"></Button>
<StackPanel Grid.Row="1" Grid.ColumnSpan="2" >
<ListBox Name="suggestionListBox" SelectionChanged="suggestionListBox_SelectionChanged" MouseDown="suggestionListBox_MouseDown"
Background="LightYellow" SnapsToDevicePixels="True"
Visibility="Collapsed"
MinWidth="150" IsHitTestVisible="True" MinHeight="70" Height="70"
VerticalAlignment="Top" LostFocus="suggestionListBox_LostFocus"/>
</StackPanel>
</Grid>
</Canvas>
</UserControl>
Your approach is not the right one to correctly manage the overlap of controls. Perhaps you may create some trick using the ZIndex property, but that won't be the solution.
If you need a drop-down control, the best way is to use a Popup control and play around it. Basically, it creates another borderless window, being child of the yours.
Another approach, maybe simpler but not good as the Popup, is using an Adorner. Maybe this one is the most similar techinique to the yours.
Cheers
Have you tried setting the ZIndex of the StackPanel to 1+ the control's zindex? That should raise the drop down portion above any other instance of your user control.
Canvas.ZIndex can be used on StackPanels.