Need ability to exit Alt key underline mode, possibly with ESC key - wpf

Question: How do you exit keyboard hot-key mode after pressing and releasing the Alt key? In Office the ESC works, in WPF it does not.
Details: I have various Label elements in a WPF application.
<StackPanel>
<Label Grid.Column="0" VerticalAlignment="Center" Content="_Textbox 1" Target="textbox1" />
<TextBox x:Name="textbox1" Width="50" />
<Label Grid.Column="0" VerticalAlignment="Center" Content="T_extbox 2" Target="textbox2" />
<TextBox x:Name="textbox2" Width="50" />
</StackPanel>
When I press and release the Alt key the P stays underlined and I can then separately press the P key and that Label takes its action. This is the same as how Word 2013 and Notepad work in that you don't have to press the Alt and the P at the same time.
Where WPF differs is I cannot press the ESC key to stop the underlining and stop the auto-navigation to Label controls. The only way I see to get out of keyboard navigation mode is to use the mouse and click somewhere, defeating the goal of keyboard navigation.

Adding to my original comment
Actually there is a slight difference in Notepad and Word 2013 behavior. The control's there with the hot-key are in a Menu which takes Focus as soon as you press Alt and you can see they get focus cos the text area caret goes missing and Esc then returns focus to the TextBox while clearing the "_". Now in your sample code if you put the two Label's or even one inside a and then press Alt, you get the same behavior as Word or Notepad. Without the Menu there is no control that wants to take the intermediate focus.
This just seems to be an issue where the Menu gets a deferred focus to handle these hotkeys in it's own FocusManager.IsFocusScope.
Another snippet from MSDN:
The following scenario illustrates how keyboard focus and logical focus change in a Windows Presentation Foundation (WPF) application that has a Window with a TextBox and a Menu which has a MenuItem. When keyboard focus changes from the TextBox to the MenuItem, the TextBox losses keyboard focus but retains logical focus for the Window focus scope. The MenuItem obtains keyboard focus and obtains logical focus for the Menu focus scope. When keyboard focus returns to the root Window, the element in Window focus scope with logical focus will obtain keyboard focus, which in this case is the TextBox. The TextBox now has keyboard focus and logical focus. The MenuItem loses keyboard focus, but retains logical focus for the Menu focus scope.
Solution:
If you just can't stand the fact that you're limited with the grouping requirement imposed by the Menu to achieve the hot-key functionality, you could do something like:
<Grid Margin="25">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="15" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- cannot use Visibility="Collapsed" or "Hidden" on the Menu to make it take focus on Alt press -->
<Menu Width="0"
Height="0">
<Label x:Name="label1"
Content="_Textbox 1"
Target="{Binding ElementName=textbox1}" />
<Label x:Name="label2"
Content="T_extbox 2"
Target="{Binding ElementName=textbox2}" />
<Label x:Name="label3"
Content="Another Loose Label to Link Text_Box 1"
Target="{Binding ElementName=textbox1}" />
</Menu>
<Label Grid.Row="0"
Grid.Column="0"
Content="{Binding ElementName=label1,
Path=Content}" />
<TextBox x:Name="textbox1"
Grid.Row="0"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="15 0 0 0"
VerticalAlignment="Center" />
<Label Grid.Row="2"
Grid.Column="0"
Content="{Binding ElementName=label2,
Path=Content}" />
<TextBox x:Name="textbox2"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="15 0 0 0"
VerticalAlignment="Center" />
<Rectangle Grid.Row="3"
Grid.RowSpan="2"
Grid.Column="1"
Margin="10"
Fill="Tomato" />
<Label Grid.Row="4"
Grid.Column="2"
Content="{Binding ElementName=label3,
Path=Content}" />
</Grid>
So in this setup we do not use any custom Style's for any of the control's. We pretty much create a new top level Menu and add all the desired hot-key Label's to it and set it's Width and Height to 0.
Thus in the actual Visible Grid layout we can position the visible Label's anywhere we choose and to avoid code duplication in specifying the Label.Content twice for each Label(Once in the Menu and next in actual layout) we use a Binding to get the Content for the Visual Label from it's corresponding Label in the Menu.
Update
Reply to OP's comment:
I'm not sure this will be practical in my application. I have hotkeys all over the app with some being duplicates inter-screen. It wouldn't be maintainable to implement this type of workaround.
Well I don't see how duplicates or having stuff all over makes any difference to this tbh.
If you have duplicate hot-key's in multiple Window's, then define Content for these duplicate item's as a String resource in xaml or your .resx you can refer to it everywhere you need it.
Maintainable - Well you do not have anything you do special for maintainability. Your layout is not impacted, your not restricted with anything. If anything you got one <Menu> sitting hidden at the Top level of each of your Window's that defines the hot-keys for that Window and within that Window any control that wants that functionality Binds to the corresponding Menu child item. I think it unifies the scope of hot-keys in a Window to a single place than in multiple different places.
Finally, I ain't trying to impose this approach on you, it's boils down to your personal preference. If your uncomfortable with it then you could get your Label's sub-classed and implement the functionality of deferred focus yourself or raise a Microsoft bug to see if they can maybe address it for you or find an alternate solution or let go of the hot-key functionality all-together.

Related

Wpf, AvalonEdit and keyboard navigation problem

I have an AvalonEdit on my window. When I press key combination Ctrl+Up or Ctrl+Down when inside editor, AvalonEdit loses focus, which is transferred to a different control, as below:
This sometimes happen as well when using Ctrl+Left or Ctrl+Right combinations.
My current XAML definition looks like following:
<ae:TextEditor x:Name="teEditor"
Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderThickness="0"
FontFamily="Consolas"
FontSize="10pt"
TabIndex="0"
WordWrap="{Binding ElementName=Root, Path=Handler.WordWrap}"
ShowLineNumbers="{Binding ElementName=Root, Path=Handler.LineNumbers}"
ContextMenu="{StaticResource EditorContextMenu}"
GotFocus="HandleEditorGotFocus"
KeyboardNavigation.ControlTabNavigation="None"
KeyboardNavigation.AcceptsReturn="True"
KeyboardNavigation.DirectionalNavigation="None"
KeyboardNavigation.TabNavigation="None"/>
How can I prevent that?
It turns out, that problem appears, when you place AvalonEdit inside TabControl. In such case you have to disable keyboard navigation on the TabControl by adding:
KeyboardNavigation.TabNavigation="Local" KeyboardNavigation.DirectionalNavigation="Contained"

How to control button layout in Xaml

Im having trouble controlling the exact layout of a button control with XAML.
It seems that whatever i do the button is of a minimum width.
I have a simple button with only a textblock inside the button. But the button has a lot of margin and padding that i cant seem to get rid of (i know of negative margins and padding).
The things i want to know is:
1. Why in the world was it designed this way.
2. what are the groundrules for controlling the exact layout of a button?
My code is as follows:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"></StackPanel>
<Pivot Grid.Row="1">
<Pivot.Title>
<StackPanel Orientation="Horizontal" Margin="-15,-3,0,0" Background="red" Width="480">
<Button Background="Blue" x:Name="btnStudies" Click="btnMenuItem_Click" Width="20">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Green">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Blue" Click="btnMenuItem_Click">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Blue" Click="btnMenuItem_Click">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Blue" Click="btnMenuItem_Click">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
</StackPanel>
</Pivot.Title>
</Pivot>
</Grid>
I want five buttons in a row but these are already too wide for the screen (windows phone). Changing the width doesnt seem to have any effect (why is it there).
The textBlock control within the button the button is as wide as the text on it, but i dont seem to have any control on the width of the button. In HTML you only have padding or margin when you define it but in xaml it just seems to be there and for me its unclear how to undo that.
*****EDIT*****
After reading Rachel's reply i decided to start from the ground up.
Using the code below i still have no control over how wide the button is because it uses a certain amount of padding that i cant seem to remove. The button has a width of about 110 when i define a width lower than that it doesnt change. Margins and paddings of 0 have no effect at all (dont want to use negative values just yet because that doesnt seem very intuitive). So the code below is very simple but still the button takes up an amount of space that i dont have any control over. I cant imagine a reason why it was designed this way.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400" />
</Grid.ColumnDefinitions>
<StackPanel Width="300" Background="Red" HorizontalAlignment="Left">
<Button Background="Blue" HorizontalAlignment="Left" Width="100" Margin="0" Padding="0">
<TextBlock Text="Title" Width="Auto" HorizontalAlignment="Left" />
</Button>
</StackPanel>
</Grid>
The type and size of the parent panel containing the control affects the size/layout of the child controls.
In your case, you have a Grid as your parent panel, and a Grid defaults to taking up all available space. In addition, children placed inside the grid default to taking up all available space as well unless you specify otherwise.
So your <Pivot> is being assigned a width equal to Grid.Width, and Pivot.Title sounds like it's being assigned a width equal to Pivot.Width, and StackPanel is being assigned a width equal to Pivot.Title.Width... you get the picture.
To specify that a control should not take up all available space, specify a HorizontalAlignment or VerticalAlignment property to tell it what side of the parent panel to dock the item on.
For example
<Pivot Grid.Row="1" HorizontalAlignment="Left">
or
<StackPanel OWidth="480" HorizontalAlignment="Left" ...>
If you're new to WPF's layout system, I would recommend reading through the codeproject article WPF Layouts: A Quick Visual Start to quickly learn what the main layout panels are for WPF.

WPF UserControl - Margins Misbehaving

I'm fairly new to WPF custom controls and have started by playing with a basic "LabelEdit" - basically a Label control and a TextBox. I have binding for 4 properties - Text, Label, TextWidth and LabelWidth (perhaps not what you would name them in a production environment, but this is just so that I can educate myself!).
All seems to work well. I also have an event which fires when the size of the label changes and causes an "ActualLabelWidth" DependencyProperty to change, so that a series of LabelEdit controls can all have the same Label Width. Here's the XAML for the LabelEdit:
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<Grid.RowDefinitions >
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding LabelWidth}" />
<ColumnDefinition Width="{Binding TextWidth}" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="{Binding Label, FallbackValue=LabelEdit}" SizeChanged="Label_SizeChanged" />
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Text}" />
</Grid>
...and for the MainWindow using it:
<ajdata:LabelEdit Text="{Binding Title}" Label="Title:" LabelWidth="{Binding ElementName=lblForename, Path=ActualLabelWidth}" TextWidth="100" />
<ajdata:LabelEdit Text="{Binding Surname}" Label="Surname:" LabelWidth="{Binding ElementName=lblForename, Path=ActualLabelWidth}" TextWidth="300" />
<ajdata:LabelEdit Text="{Binding Forename}" Label="Forename(s):" LabelWidth="Auto" TextWidth="300" Name="lblForename" />
So the label with the largest piece of text sets the width for the others.
The problem occurs when I give the Label a margin ("0,0,5,0") to space it from the TextBox element. In this situation, the LabelEdit with the "Auto" width seems to work fine. The bound versions, however, appear not to honour the margins. This means the TextBox part of the element appears positioned left of where it should be.
Does anyone know what I need to do so that all the Labels end up the same width with margins taken in to account? I realise I could probably insert an extra piece of code in my event handler, but would rather make the XAML do its job, if possible. Many thanks.
I have now solved this (but am still perhaps unsure why). Instead of setting margins of "0,0,5,0" on the label, I have set margins of "5,0,0,0" on the TextBox. This way, everything remains nicely lined up, whatever the label width.

WPF Dockpanel first child uses remaining space

In a window I have there is a list of DockPanels to specify a couple of files. Each DockPanel has a TextBox (for the path) and a button (to browse for a file).
I've recreated a simple WPF page to demostrate the problem here:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="150"
Height="22">
<DockPanel>
<TextBox HorizontalAlignment="Stretch"/> <!-- path to file -->
<Button Content="..." DockPanel.Dock="Right"/> <!-- button to browse for file -->
</DockPanel>
</Page>
The problem is that I want the button to the right of the textbox, but that causes the textbox to be really small since the LastChild of the DockPanel is the button it uses up the remaining space. Ive tried to change this by shuffling them around and setting LastChildFill="False" but that only causes the button to be small again, not making the TextBox wide (even with HorizontalAlignment="Stretch").
The reason I want the controls in that order is I want the user to reach the TextBox before the Button when using tab to navigate in the window. I looked into setting TabIndex but it feels hacky, favorite features of WPF is that tabindex are in the order the conrols are defined in the XAML. Not to mention that I probably would have to manually set TabIndex on everything in the Window.
To me, it seems that the setting of TextBox.HorizontalAlignment isn't respected. How can I make the first control use as much space as it can but still preserve tab order?
Make it like this:
<DockPanel LastChildFill="True">
<Button Content="..." DockPanel.Dock="Right"/> <!-- button to browse for file -->
<TextBox DockPanel.Dock="Left" HorizontalAlignment="Stretch"/> <!-- path to file -->
</DockPanel>
If you don't want the behavior of DockPanel, don't use a DockPanel.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox />
<Button Content="..." Grid.Column="1"/>
</Grid>

how to extend WPF GridRow Contents into next row without expanding the current row

I'm new to WPF and I'm trying to build a dropdown menu using the expander. Page layout is being handled with a Grid.
The extender sits inside the first row of the grid and I would I would like the contents of the expander to expand over top of the contents of everything below when it's clicked. Unfortunately, right now, the entire row is expanded to accommodate the height of the expanded control.
I tried playing around with the ZIndex of the Expander but it doesn't seem to have any effect. No matter what, the row always expands forcing everything else on the page to move.
<Expander FontSize="18" Name="moduleSelect" Width="100" Header=" Goto -> "
Background="#000033" MouseEnter="moduleSelect_MouseEnter"
MouseLeave="moduleSelect_MouseLeave" Foreground="White"
Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" HorizontalAlignment="Left">
<StackPanel>
<Button Name="btnTasks" Width="100" Foreground="White"
Background="#000033">Tasks</Button>
<Button Name="btnNotes" Width="100" Foreground="White"
Background="#000033">Notes</Button>
</StackPanel>
</Expander>
How can I make this expand 'above' the subsequent rows without distorting the grid?
What would happen if you set the Grid.RowSpan of the Expander to 2 (or how ever many rows you'd like it to span when expanded)?
So, for a two-row grid, you'd have something like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" /> <!--set this to the height of the expander's header area-->
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<WhateverGoesInRow2 Grid.Row="1" />
<Expander FontSize="18" Name="moduleSelect" Width="100" Header=" Goto -> "
Background="#000033" MouseEnter="moduleSelect_MouseEnter"
MouseLeave="moduleSelect_MouseLeave" Foreground="White"
Grid.Column="0" Grid.ColumnSpan="3" HorizontalAlignment="Left"
Grid.Row=0 Grid.RowSpan="2">
<StackPanel>
<Button Name="btnTasks" Width="100" Foreground="White" Background="#000033">Tasks</Button>
<Button Name="btnNotes" Width="100" Foreground="White" Background="#000033">Notes</Button>
</StackPanel>
</Expander>
</Grid>
You may need to adjust your RowDefinition section for your particular situation, but if I'm understanding your problem correctly, I think this will work.
You want something that pops up over the grid, not expands within the grid. A ComboBox, say, or - this being a menu, after all - a ContextMenu.
You could also build some combination of a ToggleButton and a Popup, but that's essentially the same thing as a ComboBox with IsEditable turned off.
The built-in drop-down control makes use of a Popup control in its default control template to do a similar thing.

Resources