I've got this xaml:
<Button VerticalAlignment="Bottom"
Click="SaveAndGoBack"
IsEnabled="{Binding Frame.CanGoBack,
ElementName=pageRoot}"
Style="{StaticResource BackButtonStyle}" />
...but on "tapping" (clicking) the back button, while it does return to the previous page, SaveAndGoBack() is not called - I have a breakpoint on the first line, and it is not reached.
The button is obviously enabled, because I am able to select it and "go back." So why would the event handler not be reached?
UPDATE
Changing clicked to tapped makes no difference. This is the XAML now:
<Button VerticalAlignment="Bottom"
Tapped="SaveAndGoBack"
IsEnabled="{Binding Frame.CanGoBack,
ElementName=pageRoot}"
Style="{StaticResource BackButtonStyle}" />
...and this is the method, with a breakpoint on the first line, which is never reached (but the Main Page is returned to):
private async void SaveAndGoBack(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs args)
{
SOs_Locations sos_locs = new SOs_Locations { GroupName = txtbxGroup.Text };
int locNum;
int.TryParse(txtbxLocationNum.Text, out locNum);
sos_locs.LocationTitle = txtbxTitle.Text;
sos_locs.CivicAddress = txtbxAddress.Text;
double lat;
double.TryParse(txtbxLatitude.Text, out lat);
sos_locs.Latitude = lat;
double lng;
double.TryParse(txtbxLongitude.Text, out lng);
sos_locs.Longitude = lng;
if (cmbxDisplayColor.SelectedValue != null)
sos_locs.LocationColor = cmbxDisplayColor.SelectedValue.ToString();
await SQLiteUtils.UpdateLocationAsync(sos_locs);
Frame.Navigate(typeof(MainPage));
}
Click is a different event from a Tap. you should look for an event called Tapped and create an OnTapped handler for it.
My guess is that for your issue. The event isn't bubbling because in the LayoutAwarePage.cs the event is set to handled.
You can modify this page to suit your needs. Look for the CoreWindow_PointerPressed method and look for the area where it sets args.Handled = true
Use the Tapped event instead as it handles click, tap and stylus events whereas Click deals specifically with the mouse (see this answer for more details).
Here's some information on how they've tried to unify the events around the different pointing input modalities around touch.
Related
I have a list of devices and in that list user will select which COM port represents which device, each device has its own StackPanel shown below:
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Width="140" Text="IMT" VerticalAlignment="Center"/>
<ComboBox Width="250" Margin="0,0,40,0" x:Name="FM_list" SelectionChanged="DeviceSelected"/>
<TextBlock x:Name="FM_selection" Margin="0,0,40,0" Width="80 "/>
<Button Background="Red" Width="50" Click="Port_selected" x:Name="FM_selection1"/>
</StackPanel>
After user makes his selection in the ComboBox it is verified by clicking an adjecent Button.
I'd like it so that when the Button is clicked x:Name of the TextBlock (or an alternate way of referencing) is passed to the Port_selected function so I can get the correct device when calling TextBox.Name on the sender.
I could a seperate x:Name for each of those buttons and a dictionary to match which button matches which TextBlock and which StackPanel, but I'd like to know how to do without that workaround. Right now I just strip the last char of the Button's x:Name...
Port_selected is an event handler and you cannot pass a string value to this one.
If you are familiar with the MVVM design pattern there are better ways to solve this, for example using commands and command parameters, but the easiest solution given your current setup is probably to get a reference to the TextBlock in the event handler and then get the value of its Name property, e.g.:
private void Port_selected(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
StackPanel stackPanel = btn.Parent as StackPanel;
if (stackPanel != null)
{
TextBlock textBlock = stackPanel.Children
.OfType<TextBlock>()
.FirstOrDefault(x => !string.IsNullOrEmpty(x.Name));
if (textBlock != null)
{
string name = textBlock.Name;
//...
}
}
}
i want to put a TextBox, together with a Label and a SymbolIcon into a Button, so that the whole thing is clickable. at the start you can only see the label and the symbol. By clicking on the button, the label gets hidden and the textBox appears, where you can type some text. By clicking again the label comes up again with the new entered text and the TextBox disappears.
My problem is, that by setting the focus to the TextBox, the button (parent) also seems to get into focus, because everytime you press the spacebar, the Click Event of the button fires. I dont want this to happen, while the TextBox has focus.
XAML
<Button Click="ToggleTopic"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<Grid>
<TextBlock x:Name="textBlockInfoTopic"
Text=""
HorizontalAlignment="Left"
Margin="100,0,100,0"/>
<TextBox x:Name="textBoxTopic"
PlaceholderText="enter Topic..."
HorizontalAlignment="Stretch"
Margin="100,0,100,0"
Visibility="Collapsed"/>
<SymbolIcon x:Name="symbolTopicButton"
Symbol="Add"
HorizontalAlignment="Right"/>
</Grid>
</Button>
C#
private void ToggleTopic(object sender, RoutedEventArgs e)
{
if (textBoxTopic.Visibility == Visibility.Visible)
{
//non edit mode
textBoxTopic.Visibility = Visibility.Collapsed;
textBlockInfoTopic.Visibility = Visibility.Visible;
symbolTopicButton.Symbol = Symbol.Add;
textBlockInfoTopic.Text = textBoxTopic.Text;
}
else
{
//edit mode
textBoxTopic.Visibility = Visibility.Visible;
textBoxTopic.Focus(FocusState.Programmatic);
textBlockInfoTopic.Visibility = Visibility.Collapsed;
symbolTopicButton.Symbol = Symbol.Go;
textBlockInfoTopic.Text = "";
}
}
I only want to prevent the triggering of the event through the spaceBar. The return key should trigger the event.
Thanks for helping me!
D.
From official document,
If a button has keyboard focus, pressing the Enter key or the Space key also raises the Click event. You generally can't handle low-level PointerPressed events on a Button because it has the Click behavior instead.
If you want to prevent SpaceBar from triggering the Click event, you could override OnProcessKeyboardAccelerators method of Button like the following.
public class MyButton : Button
{
protected override void OnProcessKeyboardAccelerators(ProcessKeyboardAcceleratorEventArgs args)
{
if(args.Key == VirtualKey.Space)
{
args.Handled = true;
}
base.OnProcessKeyboardAccelerators(args);
}
}
Usage
<local:MyButton Click="ToggleTopic"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<Grid IsTapEnabled="False" >
<TextBlock x:Name="textBlockInfoTopic"
Text=""
HorizontalAlignment="Left"
Margin="100,0,100,0"/>
<TextBox x:Name="textBoxTopic"
PlaceholderText="enter Topic..."
HorizontalAlignment="Stretch"
Margin="100,0,100,0"
Visibility="Collapsed"/>
<SymbolIcon x:Name="symbolTopicButton"
Symbol="Add"
HorizontalAlignment="Right"/>
</Grid>
</local:MyButton>
I am trying to make a small modal window with a search box and a list of results.
My goal is for the focus to land in the first element of the List after searching.
Problem
Unfortunately, I stuck in a situation where my focus lands on the window itself after searching.
My TextBox has an InputBinding that triggers on enter to perform the search:
<TextBox Grid.Column="1" Grid.Row="1"
Text="{Binding Supplier.LeverandorNavn, UpdateSourceTrigger=PropertyChanged}"
x:Name="txtBoxSupplierName" Width="150"
HorizontalAlignment="Left" VerticalAlignment="Top"
TabIndex="1">
<TextBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding SearchCommand}" />
</TextBox.InputBindings>
</TextBox>
I have been trying to use Snoop to understand the flow of events.
As seen here, the KeyDown is handled by my txtBoxSupplerName Textbox fine.
But I cannot understand why the levWindow gets focus after my command executes.
I have attempted to manually set the focus element, but it has no effect.
Questions
Can someone explain to me, why the focus lands on the window by default?
Can someone suggest an approach, on how I can take control of the focus myself and avoid this default behavior?
I am challenged by the fact that the DataGrid needs some time to redraw itself before the row I want to focus is visible.
Properties
public ClientLeverandor ValgtLeverandør
{
get { return _valgtLeverandør; }
set { SetProperty(ref _valgtLeverandør, value, nameof(ValgtLeverandør)); }
}
public ObservableCollection<ClientLeverandor> Leverandører
{
get { return _leverandører; }
private set { SetProperty(ref _leverandører, value, nameof(Leverandører)); }
}
public ListCollectionView LeverandørView
{
get { return _leverandørView; }
set { SetProperty(ref _leverandørView, value, nameof(LeverandørView)); }
}
Command Implementation
using (BusyIndicator.ShowInScope(Strings.HenterData_BusyText))
{
var leverandører = await SøgLeverandører(Supplier);
if (leverandører.Any())
{
Leverandører.Clear();
foreach (var lev in leverandører) Leverandører.Add(lev);
ValgtLeverandør = Leverandører[1];
SuppliersAdded?.Invoke();
}
}
I was unable to find an explanation for why KeyboardFocus land on my window or why my attempts to set the focus did not have effect.
But i was finally able to find a solution, where my attempt worked.
I used a Dispatcher with BackgroundPriority to invoke my method to set focus after whatever was setting focus to the window. This worked for both Keyboard and Mouse input.
private void ViewModel_SuppliersAdded()
{
Dispatcher.BeginInvoke(new Action(SetFocusToFirstRow), DispatcherPriority.Background);
}
I have a hub control with two HubSections. When selected HubSection changes, I want to change the contents of the AppBar with section specific buttons.
Listening SectionsInViewChanged event is the general solution recommended to implement this behavior but this event is not fired when there are only two HubSections.
Is there another event that can be used to determine the current HubSection?
Thanks.
#Depechie has pointed you in the right direction.. You can use the SelectionHub control I created and add an event to it that fires when the selected index changes
public event EventHandler SelectionChanged;
private static void OnSelectedIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var hub = d as SelectionHub;
if (hub == null) return;
// do not try to set the section when the user is swiping
if (hub._settingIndex) return;
// No sections?!?
if (hub.Sections.Count == 0) return;
hub.OnSelectionChanged();
}
private void OnSelectionChanged()
{
var section = Sections[SelectedIndex];
ScrollToSection(section);
var handler = SelectionChanged;
if(handler != null)
{
handler(this, EventArgs.Empty);
}
}
You can extend this by adding selection changed event args to it. With this you can then subscribe to the event in your code behind and change the app bar buttons based on the index.
After reading Shawn's article suggested by #Depechie. I tried to implement the same solution in my app in order to update contents of the AppBar with section specific buttons.
Despite my efforts I was unable to make it work so I modified some parts of the solution. I used the behavior solution and changed only the ScrollerOnViewChangedfunction as follows. This might not be the best way or may cause unexpected results in different scenarios but in my case it worked without a problem.
private void ScrollerOnViewChanged(object sender, ScrollViewerViewChangedEventArgs scrollViewerViewChangedEventArgs)
{
_settingIndex = true;
ScrollViewer scrollViewer = sender as ScrollViewer;
if (scrollViewer.HorizontalOffset > (scrollViewer.ViewportWidth / 2))
SelectedIndex = 1;
else
SelectedIndex = 0;
_settingIndex = false;
}
After that I added a property to my viewmodel in order to store selected index.
private int _selectedIndex;
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
SetProperty(ref this._selectedIndex, value);
}
}
I used the behavior in the XAML in order to update SelectedIndex in my ViewModel.
<Hub>
<i:Interaction.Behaviors>
<behaviors:HubSelectionBehavior SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}" />
</i:Interaction.Behaviors>
<HubSection>...</HubSection>
<HubSection>...</HubSection>
</Hub>
The last thing to do was to set the visibility of AppBarButtons using this property. SectionIndexToVisibilityConverter compares SelectedIndex to ConverterParameter and returns Visibility.Visible if they are equal.
<CommandBar>
<AppBarButton Label="Open" Icon="World" Command="{Binding OpenInBrowserCommand}" Visibility="{Binding SelectedIndex, Converter={StaticResource SectionIndexToVisibilityConverter}, ConverterParameter=0}"/>
<AppBarButton Label="Previous" Icon="Back" Command="{Binding PreviousAnswerCommand}" Visibility="{Binding SelectedIndex, Converter={StaticResource SectionIndexToVisibilityConverter}, ConverterParameter=1}"/>
<AppBarButton Label="Next" Icon="Forward" Command="{Binding NextAnswerCommand}" Visibility="{Binding SelectedIndex, Converter={StaticResource SectionIndexToVisibilityConverter}, ConverterParameter=1}"/>
</CommandBar>
Thanks #Depechie for suggesting the article and #Shawn for writing the article : )
Some of the checkboxes on my form should not be able to be checked/unchecked by users. Is there a way for me to cancel the event before the checbox's Check event is triggered?
in winForms it was easy, just
public void cb_BeforeChecked(object sender, EventArgs e){
e.Handled = true;
}
but I cannot find anything like this in WPF...I figure you can probably do it, just need to do something fancy..
Thanks!
Why not just set IsEnabled="False"?
You can set IsHitTestVisible="False" to make it not respond to user clicks. Otherwise you can bind it to a command if viewmodel logic determines whether it is clickable.
<Grid>
<CheckBox IsHitTestVisible="False" Content="I cannot be clicked at all"/>
<CheckBox Command="{Binding DoSomethingCommand}" Content="I can be clicked if DoSomethingCanExecute returns true."/>
</Grid>
In your DataContext (Viewmodel or otherwise):
RelayCommand _DoSomethingCommand = null;
public ICommand DoSomethingCommand
{
get
{
if (_DoSomethingCommand== null)
{
_DoSomethingCommand= new RelayCommand(
param => DoSomething(),
param => DoSomethingCanExecute
);
}
return _DoSomethingCommand;
}
}
public bool DoSomethingCanExecute
{
get
{
return CheckboxShouldBeEnabled();
}
}
public void DoSomething()
{
//Checkbox has been clicked
}
This might be a bit of an overkill, but you could sub-class CheckBox and then override the OnClick() method.
Only setting IsHitTestVisible="False" just takes care of the Mouse, the users can still use the KeyBoard to tab to the CheckBox and change the value.
You should set both IsHitTestVisible="False" and Focusable="False" to disable the KeyBoard as well
You can have the check box disabled, and associate a style with the disabled check box, if the disabled look is a problem. As its already pointed in the previous posts, its good to have different looks for different states.