Textbox not losing focus when tapping anywhere inside Popup control - xaml

I have a Popup with a Textbox and a Button control in it.
When I click/tap anywhere inside the Popup, the Textbox doesn't lose focus.
The only way to make the Textbox lose focus is to set focus on the Button.
I want to be able to remove focus from the Textbox without having to close the Popup or clicking the button.
This issue only occurs in a Popup.
I used to have the controls in a Flyout, and the behavior there is when a user clicks outside the Textbox and anywhere inside the Flyout, the Textbox loses focus. Also when the Flyout opens, the Textbox would automatically get focus on Flyout opening.
I tested the different behaviors for how Textboxes losing focus in a new blank UWP project, and it's the same. Also tested behavior of Textbox directly in root-grid. This is the XAML from MainPage:
<Page x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:App1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Margin="0,40"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Orientation="Horizontal">
<Button Margin="20,0"
Content="Popup button"
Tapped="Button_Tapped" />
<Button Margin="20,0" Content="Flyout button">
<Button.Flyout>
<Flyout>
<Grid Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox VerticalAlignment="Center" />
<Button Grid.Column="1" Content="Test" />
</Grid>
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
<Popup x:Name="MyPopup"
Width="320"
Height="60"
IsLightDismissEnabled="True">
<Grid Width="320"
Height="60"
Background="WhiteSmoke"
Padding="12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox VerticalAlignment="Center" />
<Button Grid.Column="1" Content="Test" />
</Grid>
</Popup>
<TextBox Grid.Row="1"
MinWidth="64"
MaxWidth="300"
Margin="0,40"
VerticalAlignment="Center" />
</Grid>
</Page>
And the event to open the Popup:
private void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
MyPopup.IsOpen = true;
}

The problem is that Grid is not "focusable" because it does not derive from Control. One way is that you place your Grid inside a ContentControl or ContentPresenter.
The other way is a bit of a hack but you can set the focus programatically on the button when the Grid inside the Popup is tapped:
<Popup
x:Name="MyPopup"
Width="320"
Height="60"
IsLightDismissEnabled="True">
<Grid
x:Name="Grid"
Width="320"
Height="60"
Background="WhiteSmoke"
Tapped="Grid_OnTapped"
Padding="12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox VerticalAlignment="Center" />
<Button
Grid.Column="1"
Content="Test" />
</Grid>
</Popup>
And the Tapped handler in which you generally search for any focusable control that is not a TextBox. If you don't have any you can place an invisible ContentControl with Opacity=0.01 for example and this will then be the focus element:
private void Grid_OnTapped(object sender, TappedRoutedEventArgs e)
{
var grid = sender as Grid;
if(grid == null) return;
var controlToFocus = FindChild<Button>(grid);
controlToFocus.Focus(FocusState.Programmatic);
}
private static T FindChild<T>(DependencyObject parent) where T : DependencyObject
{
var childCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childCount; i++)
{
var elt = VisualTreeHelper.GetChild(parent, i);
if (elt is T) return (T)elt;
var result = FindChild<T>(elt);
if (result != null) return result;
}
return null;
}

Related

Avalonia How To Create Custom Popup

A little explanation first:
This is source code of Popup element
And this is example of using Popup DatePicker and it's xaml
I've tried use this example on empty window like this
Here is result:
So I want to create a OK/Cancel pick panel and use like DateTimePicker Pop and Select what do you want and accept or abort the selections.
How can I create a simple custom picker like this.
Edit: Here is My DateTimePicker and screenshots
After Tap
Ok I analyzed the existing codes for hours. Simply the answer is:
Step 1: Create a presenter class for manage your controls and events for popup. (like DatePickerPresenter
namespace AvaloniaTest.Views {
public class SabriPresenter : PickerPresenterBase
{
...
}
}
Step 2: Create Template for this presenter in your window or resource.
<Window xmlns="bla bla bla"
xmlns:v="using:AvaloniaTest.Views"
Title="AvaloniaTest">
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
<Window.Styles>
<Style Selector="v|SabriPresenter" >
<Setter Property="Template">
<ControlTemplate>
<Grid Background="AliceBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Panel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2">
<TextBlock Text="Your content..."/>
</Panel>
<Button Grid.Column="0" Grid.Row="0" Content="OK"/>
<Button Grid.Column="1" Grid.Row="0" Content="Cancel"/>
</Grid>
</ControlTemplate>
</Setter>
</Style>
</Window.Styles>
</Window>
Step 3: Use this presenter wherever you want
<Window xmlns="bla bla"
xmlns:v="using:AvaloniaTest.Views"
>
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button Content="Popeye" Name="sabri"/>
<Popup Name="Popup"
WindowManagerAddShadowHint="False"
StaysOpen="False"
PlacementMode="Bottom"
PlacementTarget="{Binding ElementName=sabri}"
>
<v:SabriPresenter />
</Popup>
</StackPanel>
</Window>
Ta-daa!

how do I make a slide effect in a listview? - UWP

I do not know how to do the effect very well, and I know how to capture the StackPanel to which I am going to make the effect. To help me with this last would be very helpful. What I want is to hide all StackPanel from a ListView and only show the element that is clicked on, if it loses focus, it will hide again. Here is the essential.
<ListView Grid.Row="1" ItemsSource="{Binding listOfSomething, Mode=TwoWay}" SelectionChanged="ListView_SelectionChanged">
<!-- rest of code -->
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Margin="0,2,0,2">
<!-- rest of code -->
<StackPanel Grid.Column="2" Grid.RowSpan="2" Background="#FFF" Margin="5,0,0,0">
<Grid Background="Transparent" Height="52">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Background="Transparent" Padding="0,0,0,0">
<StackPanel Padding="0,0,0,0" Margin="0,0,0,0">
<Image Source="ms-appx:///Assets/plus.png" Width="32" Height="32" />
</StackPanel>
</Button>
<Button Grid.Column="1" Background="Transparent" Padding="0,0,0,0">
<StackPanel Padding="0,0,0,0" Margin="0,0,0,0">
<Image Source="ms-appx:///Assets/delete.png" Width="32" Height="32"/>
</StackPanel>
</Button>
</Grid>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I do this approach but I think it's not recommended for long lists as it tries to suspend/maintain the view, but it's worth a shot based on your desired outcome/effect:
So here it is, first of all, create two DataTemplates inside your Page.xaml
<Page.Resources>
<DataTemplate x:Name="normalTemplate" x:Key="normalTemplate">
<!-- Your template view when item is NOT clicked/selected -->
</DataTemplate>
<DataTemplate x:Name="selectedTemplate" x:Key="selectedTemplate">
<!-- Your template view when item is clicked/selected -->
</DataTemplate>
</Page.Resources>
Modify your ListView control with:
<ListView Grid.Row="1" ItemsSource="{Binding listOfSomething, Mode=TwoWay}" SelectionChanged="ListView_SelectionChanged" ItemTemplate="{StaticResource normalTemplate}" />
Then on your ListView_SelectionChanged method, insert/add this lines of code:
try
{
// Assign DataTemplate for selected items
foreach (var item in e.AddedItems)
{
ListViewItem lvi = (sender as ListView).ContainerFromItem(item) as ListViewItem;
lvi.ContentTemplate = (DataTemplate)this.Resources["selectedTemplate"];
}
//Remove DataTemplate for unselected items
foreach (var item in e.RemovedItems)
{
ListViewItem lvi = (sender as ListView).ContainerFromItem(item) as ListViewItem;
lvi.ContentTemplate = (DataTemplate)this.Resources["normalTemplate"];
}
}
catch (Exception ex)
{ }

Only capture tap event from button contained in ListViewItem without triggering item click - UWP Windows 10

In my UWP app which uses MVVMLight, I have a button contained in my ListViewItem and when clicked I want to display a flyout and provide additional actions for the specific ListViewItem but whenever I tap on the button, I get an error i.e.
System.InvalidCastException: Unable to cast object of
type 'Windows.UI.Xaml.Input.TappedRoutedEventArgs' to
type 'MyApp.ViewModels.ItemViewModel'.
at GalaSoft.MvvmLight.Command.RelayCommand`1.Execute(Object parameter)
at Microsoft.Xaml.Interactions.Core.InvokeCommandAction.Execute(Object
sender, Object parameter) at
Microsoft.Xaml.Interactivity.Interaction.ExecuteActions(Object sender,
ActionCollection actions, Object parameter)
at Microsoft.Xaml.Interactions.Core.EventTr
I understand to some extend why it's happening but how I can I fix this? The button contained in my ListViewItem data template is obviously being passed to the parent which is the listview and the DataTrigger that's defined in the Listview is capturing it.
All I want to do is display a flyout when this button is clicked to provide additional options.
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="{StaticResource SystemControlBackgroundAccentBrush5}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="4"
Width="90"
Height="90"
VerticalAlignment="Center"
Margin="5,5,0,5">
<Image Width="90"
Height="90"
Source="{Binding Logo}" />
</Border>
<Grid Grid.Row="0" Grid.Column="1" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"
Grid.Row="1" VerticalAlignment="Bottom">
<TextBlock Text="Id:" FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Text="{Binding Subject}" Margin="10,0,0,0" Foreground="White" VerticalAlignment="Center"/>
</StackPanel>
<Button Width="30" Height="30"
Command="{Binding AdditionalInfoCommand}"
Grid.RowSpan="4"
Grid.Column="1"
Margin="0,0,12,0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Stroke="{ThemeResource SystemControlBackgroundAccentBrush}"
Fill="{ThemeResource SystemControlBackgroundAccentBrush}"
StrokeThickness="2">
</Ellipse>
<Image Source="ms-appx:///Assets/Information.png" Width="30" Height="30" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
and my ListView has the following code:
<ListView ItemsSource="{Binding MyList}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionMode="Single">
<ListView.ItemTemplate>
.... as defined above
</ListView.ItemTemplate>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ItemClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
Any ideas on how I can resolve this?
UPDATE 1:
I'll re-phrase my question slightly. I've managed to bind the button displayed on the ListViewItem to a specific click event by introducing a RelayCommand (AdditionalInfoCommand) in the item's ViewModel. So now my main problem is the actual error mentioned above which is triggered after AdditionalInfoCommand and I assume it's because it is being captured by the ItemClickCommand. Strange as you would assume that it is using the same ViewModel.
Is there a way to no trigger the itemclick when this button is tapped?
Thanks.
Thierry
I figured it out.
When I tap the button inside my ListView DataTemplate, the button is being passed the Item's ViewModel which is correct but then the tap event is bubbled up back to the parent which is captured by:
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ItemClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
While there may be a solution to stop the event bubbling, to circumvent the problem, I changed my ItemClickCommand.
It was defined as follows:
private RelayCommand<MyItemViewModel> _itemClickCommand;
public RelayCommand<MyItemViewModel> ItemClickCommand
{
get { return this._itemClickCommand ?? (this._itemClickCommand =
new RelayCommand<MyItemViewModel>((viewModel) =>
ItemClickCommandAction(viewModel))); }
}
I simply changed it to:
private RelayCommand<object> _itemClickCommand;
public RelayCommand<object> ItemClickCommand
{
get { return this._itemClickCommand ?? (this._itemClickCommand =
new RelayCommand<object>((viewModel) =>
ItemClickCommandAction(viewModel))); }
}
I then changed the ItemClickCommandAction to take in an object instead of MyItemViewModel and now, within the method, I check the type of the ViewModel being passed:
private void ItemClickCommandAction(object currentObject)
{
if (currentObject is MyItemViewModel)
{
//go to next page
}
}

UWP - adding items to ListView dynamically cause crash

I have a listview defined like this:
<ListView
x:Name="phonesListView"
Grid.Row="5"
Background="Black"
IsItemClickEnabled="True"
Foreground="Gray" >
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="auto" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid
Grid.Row="0">
<Grid.ColumnDefinitions >
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ComboBox
Grid.Column="0"
VerticalAlignment="Center"
Background="Black"
Foreground="White">
<ComboBoxItem Content="Home" IsSelected="True"/>
<ComboBoxItem Content="{Binding Type}"/>
<ComboBoxItem Content="Office"/>
<ComboBoxItem Content="Fax"/>
</ComboBox>
<Button
Grid.Column="1"
Height="30"
Width="30"
Foreground="Black"
Margin="0, 5, 0, 5"
HorizontalAlignment="Center" Click="RemovePhone">
<Button.Background>
<ImageBrush Stretch="Fill" ImageSource="Assets/appbar.close.png" />
</Button.Background>
</Button>
</Grid>
<TextBox
Grid.Row="1"
Background="White"
Foreground="Black"
FontSize="20"
InputScope="TelephoneNumber"
Text="{Binding Number}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have an private ObservableCollection<Phone> numbers = new ObservableCollection<Phone>();
In constructor I call phonesListView.ItemSource = numbers;
And on some button click I want to add new item to the listView so I call a method:
private void AddPhone(object sender, RoutedEventArgs e) {
Phone phone = new Phone("", Types.HOME);
numbers.Add(phone);
}
But after button click to add a item the app crashes and App.g.i.cs is called and global::System.Diagnostics.Debugger.Break(); is highlighted
#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
UnhandledException += (sender, e) =>
{
if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
};
#endif
I'm really new to Universal Windows App, and I read that this is called when something is wrong with XAML code. Could you help me with that? Thanks.
I tried to use ItemsSource="{x:Bind Mode=TwoWay}"
The only way (but not the best, I think) where I could dynamically update a listview was making a method that handle a button event where I add a new element (calling the addmethod) and updating the ItemsSource property of the list view.
private void AnadeDoctorAppBarButton_Click(object sender, RoutedEventArgs e)
{
Doctor A = new Doctor("Mapache", "dentista", "La calle", "La vida", "Lo que sea", "Direccion", "olos");
ViewModel.AnadeDoctor = A;
ListaDePrueba.ItemsSource = ViewModel.ColeccionDoctores;
}
It Works, but I think that is not true Databinding.

Capture Element not using room from parent control

I am trying to create a page with a capture element and a button overlaid on top to take the picture.
The problem I've got is, the capture element won't use up the whole screen. There are bars above and below.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<CaptureElement
Grid.Row="0"
x:Name="capPreview"
Stretch="Uniform"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Tapped="capPreview_Tapped"
/>
<Button Content="Submit"
HorizontalAlignment="Center"
Grid.Row="1"
x:Name="btnTakePicture"
Click="btnTakePicture_Click">
<Button.Background>
<SolidColorBrush Color="Black" Opacity="100"/>
</Button.Background>
<Button.BorderBrush>
<SolidColorBrush Color="White"/>
</Button.BorderBrush>
</Button>
</Grid>
Edit: I have since used the code behind to rotate the capture element, however; upon doing so, the element now just has a box all around it instead of top and bottom.
<CaptureElement
Grid.Row="0"
x:Name="capPreview"
Tapped="capPreview_Tapped"
/>
Short of hard coding the height and width values, I am out of ideas.
It deos use the whole screen, the issue is the orientation of the preview is different than the orientation of the screen.
MediaCapture has a method SetPreviewRotation to handle this issue.
It works for me :) Just grid and Strech, If you want to have focus just make transparent grid outside CaptureElement
<Grid>
<Grid Tapped="SetFocus">
<CaptureElement x:Name="CaptureElement" Stretch="UniformToFill"/>
</Grid>
<Grid VerticalAlignment="Bottom">
<Button x:Name="SaveButton" Foreground="Transparent" IsDoubleTapEnabled="False" Width="48" Height="48" Background="Transparent" BorderBrush="Transparent"
Click="OnSaveButtonClicked" HorizontalAlignment="Stretch" Margin="12,0">
</Button>
</Grid>
</Grid>
Try this one it will capture directly with the button click
private async void Button_Click_3(object sender, RoutedEventArgs e)
{
Uri u = new Uri("ms-data:///local");
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("Photo.jpg");
IRandomAccessStream st = await file.OpenAsync(FileAccessMode.Read);
BitmapImage image = new BitmapImage();
image.SetSource(st);
img.Source = image;
}
<CaptureElement Name="element" Height="600"/>
<StackPanel>
<Button Content="Capture Image" Click="Button_Click_3"/>
</StackPanel>
<Image Name="img" Margin="0,0,0,0"/>