WPF - Holding modifier keys slows application - wpf

This is a vb.net WPF application.
Holding modifier keys [Shift, Alt, Ctrl] slows this application.
My code rotates a cube when Key.Q is held down. Pressing Shift+Q, Alt+Q or Ctrl+Q visibly slows down the rotation.
Why is this happening and how to I disable the background key press function that's being called?
Edit - I've tried handling key presses for the modifier keys, i.e. adding variations of "'If e.Key = Key.LeftShift Or e.Key = Key.RightShift Then..." to the key press events but these have not helped.
<Viewport3D Name="ViewportMain">
<Viewport3D.Camera>
<PerspectiveCamera x:Name="camMain" Position="6 5 4" LookDirection="-6 -5 -4">
</PerspectiveCamera>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight Color="#ffffff" />
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D x:Name="MyModel">
<ModelVisual3D.Content>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D x:Name="meshMain"
Positions="0 0 0 1 0 0 0 1 0 1 1 0 0 0 1 1 0 1 0 1 1 1 1 1"
TriangleIndices="2 3 1 2 1 0 7 1 3 7 5 1 6 5 7 6 4 5 6 2 0 2 0 4 2 7 3 2 6 7 0 1 5 0 5 4">
</MeshGeometry3D>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial x:Name="matDiffuseMain">
<DiffuseMaterial.Brush>
<SolidColorBrush Color="Red"/>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
</GeometryModel3D>
</ModelVisual3D.Content>
<ModelVisual3D.Transform>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D x:Name="rotate" Axis="0 2 0"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
</ModelVisual3D.Transform>
</ModelVisual3D>
</Viewport3D>
Public WithEvents Timer1 As New System.Windows.Threading.DispatcherTimer()
Public ax3d As Media3D.AxisAngleRotation3D
Public myRotateTransform As Media3D.RotateTransform3D
Public isrotating As Boolean = False
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
ax3d = New Media3D.AxisAngleRotation3D(New Media3D.Vector3D(0, 2, 0), 1)
myRotateTransform = New Media3D.RotateTransform3D(ax3d)
MyModel.Transform = myRotateTransform
Timer1.IsEnabled = True
Timer1.Interval = TimeSpan.FromMilliseconds(1)
Timer1.Start()
End Sub
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
If isrotating Then
ax3d.Angle += 4
End If
End Sub
Private Sub Window_KeyDown(sender As Object, e As KeyEventArgs)
If e.IsRepeat Then
e.Handled = True
Return
End If
If e.Key = Key.Q Then
isrotating = True
e.Handled = True
End If
e.Handled = True
End Sub
Private Sub Window_KeyUp(sender As Object, e As KeyEventArgs)
If e.Key = Key.Q Then
isrotating = False
e.Handled = True
End If
End Sub

Setting the priority for the DispatcherTimer to Render priority fixed the issue. The default priority is Background.
"Operations are processed after all other non-idle operations are completed."
Holding the modifier keys Shift, Alt, and Ctrl, as well as CapsLock, would interfere with the DispatcherTimer and cause the timer ticks to be delayed.
Setting the priority to Render allows operations to be processed at the same priority as rendering. As a result the animation no longer stutters.

Related

Datagrid focus to selected row

I have a simple datagrid with some itemssource. I select a row and go to other control. Every time i focus the grid again using tab, it selects the first row and cell. (It show a black border box on first cell)
I have set the IsSynchronizedWithCurrentItem="True" but not working
I want the previous selected row to get focus.
"it selects the first row and cell" no in fact the selection remains as it was but yes when the focus returns to datagrid the focused cell will change to the first cell.
Check if this approach suits you, it's the simplest I came with so far!:
First, add event handler to cell GotFocus and LostFocus as follows:
<DataGrid x:Name="list_1">
<DataGrid.Columns >
<DataGridTextColumn Header="ID" Width="30" Binding="{Binding id}"/>
<DataGridTextColumn Header="Name" Width="50" Binding="{Binding name}"/>
<DataGridTextColumn Header="UserID" Width="50" Binding="{Binding user_id}"/>
</DataGrid.Columns>
<DataGrid.CellStyle >
<Style TargetType="DataGridCell" >
<EventSetter Event="GotFocus" Handler="cgf"/>
<EventSetter Event="LostFocus" Handler="clf"/>
</Style>
</DataGrid.CellStyle>
</DataGrid>
In your code behind, add the following code:
Private WithEvents timer1 As New Threading.DispatcherTimer With {.Interval = New TimeSpan(0, 0, 0, 0, 50)}
Private WithEvents timer2 As New Threading.DispatcherTimer With {.Interval = New TimeSpan(0, 0, 0, 0, 50)}
Dim LastFocusedCell As DataGridCell
Dim focus_lost As Boolean
Sub cgf(sender As object, e As RoutedEventArgs)
timer1.Stop()
If focus_lost Then
focus_lost = False
If LastFocusedCell IsNot Nothing Then
list_1.IsReadOnly = True
LastFocusedCell.Focus()
timer2.Start()
Else
LastFocusedCell = sender
End If
Else
LastFocusedCell = sender
End If
End Sub
Sub clf(sender As Object, e As RoutedEventArgs)
timer1.Start()
End Sub
Private Sub tmr_Tick(sender As Object, e As EventArgs) Handles timer1.Tick
timer1.Stop()
focus_lost = True
End Sub
Private Sub tmr2_Tick(sender As Object, e As EventArgs) Handles timer2.Tick
timer2.stop()
list_1.IsReadOnly = False
End Sub
I don't think it's the best approach but it worked fine for me.

WPF handle Pinch Zoom on Touchpad

I have a WPF TextBlock. I am controlling the font size by using ManipulationDelta, to simulate zooming in/out. It's currently in a very crude way, but it works.
This works for a touchscreen, but I want the same behaviour to work on my laptop touchpad - i.e. two-finger pinch-zoom.
What's the event I need to hook into to make this work?
The touch code is as follows:
<TextBlock Name="txbl_displaySong" Foreground="White" FontFamily="Lucida Console"
FontSize="22" Text="{Binding Path=T.SelectedSong.TransposedChordsOverLyrics, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Padding="20" TextWrapping="Wrap" IsManipulationEnabled="True"
ManipulationDelta="txbl_displaySong_ManipulationDelta"
ManipulationCompleted="Txbl_displaySong_ManipulationCompleted"
TouchDown="Txbl_displaySong_TouchDown"
MouseDown="Txbl_displaySong_MouseDown"/>
Code Behind:
Dim isPinchIn As Boolean = False
Dim isPinchOut As Boolean = False
Dim prevScale As Double = 0
Private Sub Txbl_displaySong_ManipulationCompleted(sender As Object, e As ManipulationCompletedEventArgs)
prevScale = 0
isPinchIn = False
isPinchOut = False
End Sub
Private Sub txbl_displaySong_ManipulationDelta(sender As Object, e As ManipulationDeltaEventArgs)
Dim tScale As Double = e.CumulativeManipulation.Scale.Length
If (prevScale > 0) Then
isPinchIn = isPinchIn OrElse prevScale > tScale ' if same scale, assume no change
isPinchOut = isPinchOut OrElse prevScale < tScale ' if same scale, assume no change
End If
prevScale = tScale
Dim mCount As Integer = e.Manipulators.Count
...
If mCount = 2 Then
If isPinchIn Then
txbl_displaySong.FontSize = Math.Max(6.0, txbl_displaySong.FontSize - 0.2) ' limit to 6
ElseIf isPinchOut Then
txbl_displaySong.FontSize = Math.Min(40.0, txbl_displaySong.FontSize + 0.2) ' limit to 40
Else
End If
End If
End Sub
...
I have the same problem, and I figured out a workaround which half solves it:
Wire up some code to the MouseWheel event:
private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta > 0)
...
else if (e.Delta < 0)
...
}
With my touchpad, I get a Delta of either 120 or -120 depending on whether I'm pinching to zoom in or pinching to zoom out.

MatrixTransform rotate then translate image in xaml

I'm trying to rotate then translate an image with the simple Matrix.translate and Matrix.rotate methods, but for some reason when it's rotated 90º or 270º, if I try to translate it even an inch it spins out of control and away from the center. And if it's 180º it shoots out in the opposite direction.
The xaml for the image is like so:
<Border x:Name="Border" Grid.Row="0" MinHeight="100" MinWidth="100" VerticalAlignment="Center" HorizontalAlignment="Center" ClipToBounds="True" BorderBrush="Black" BorderThickness="1" >
<Image x:Name="imgImagem" MaxHeight="550" MaxWidth="950"
Margin="10,10,10,10" MouseWheel="imgImagem_MouseWheel"
MouseLeftButtonDown="imgImagem_MouseLeftButtonDown"
/>
</Border>
For the rotation:
Private Sub imgImagem_MouseRightButtonDown(sender As Object, e As MouseButtonEventArgs) Handles imgImagem.MouseRightButtonDown
Dim m As Matrix = imgImagem.RenderTransform.Value
m.RotateAt(90, imgImagem.ActualWidth / 2, imgImagem.ActualHeight / 2)
imgImagem.RenderTransform = New MatrixTransform(m)
End Sub
And for the translation:
Private Sub imgImagem_MouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs)
If imgImagem.IsMouseCaptured Then
Return
End If
imgImagem.CaptureMouse()
start = e.GetPosition(imgImagem)
origin.X = imgImagem.RenderTransform.Value.OffsetX
origin.Y = imgImagem.RenderTransform.Value.OffsetY
End Sub
Private Sub imgImagem_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles imgImagem.MouseLeftButtonUp
imgImagem.ReleaseMouseCapture()
End Sub
Private Sub imgImagem_MouseMove(sender As Object, e As Input.MouseEventArgs) Handles imgImagem.MouseMove
Dim m As Matrix = imgImagem.RenderTransform.Value
If Not imgImagem.IsMouseCaptured Then
Return
End If
Dim p As Point = e.MouseDevice.GetPosition(imgImagem)
m.Translate((p.X - start.X), (p.Y - start.Y))
imgImagem.RenderTransform = New MatrixTransform(m)
End Sub
Any clues on what's making it spin out and such or what am I doing wrong for it to present such behavior?
Just had to differentiate the translation depending on the angle like so:
x = p.X - start.X
y = p.Y - start.Y
Select Case angulo
Case 0
m.Translate(x, y)
Case 90
m.Translate(-y, x)
Case 180
m.Translate(-x, -y)
Case 270
m.Translate(y, -x)
End Select

Print input from user in WPF VB

I'm working on project where the user will enter a JobNumber, say (J000001), and when the user hits print the jobnumber will print. With the code below, I'm able to print the numbers, say (001), but I want the user to enter the actual JobNumber (J000001). Any help on this is greatly appreciated.
When I enter the JobNumber (J000001), I get the following error message:
'Invalid CastException was unhandled'
Conversion from string "J000001" to type boolean is not valid.
Below is my VB Code:
Imports System.Globalization
Imports System.Drawing.Printing
Imports System.Drawing
Class MainWindow
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
AddHandler printDocument1.PrintPage, AddressOf printDocument1_PrintPage
End Sub
'Declaration the global variables
Private paperSize As New PaperSize("papersize", 300, 500)
'set the paper size
Private totalnumber As Integer = 0
'this is for total number of items of the list or array
Private itemperpage As Integer = 0
'this is for no of item per page
Private printDocument1 As New PrintDocument()
Private printDialog1 As New System.Windows.Forms.PrintDialog()
Private DefaultFont As New Font("Calibri", 20)
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
If txtStart.Text Then
itemperpage = 1
totalnumber = txtStart.Text
printDialog1.Document = printDocument1
printDocument1.DefaultPageSettings.PaperSize = paperSize
printDialog1.ShowDialog()
'printDocument1.PrinterSettings.PrinterName = "";
printDocument1.Print()
Else
MessageBox.Show("Invalid number")
End If
End Sub
Private Function CheckNumber(str As String)
Dim Num As Double
Return Double.TryParse(str, Num)
End Function
'Define the Printpage event of the printdocument
Private Sub printDocument1_PrintPage(sender As Object, e As System.Drawing.Printing.PrintPageEventArgs)
Dim currentY As Single = 10
While totalnumber <= CInt(txtStart.Text)
' check the number of items
e.Graphics.DrawString(totalnumber.ToString(), DefaultFont, System.Drawing.Brushes.Black, 50, currentY)
'print each item
currentY += 20
' set a gap between every item
totalnumber += 1
'increment count by 1
If itemperpage < 1 Then
' check whether the number of item(per page) is more than 1 or not
itemperpage += 1
' increment itemperpage by 1
' set the HasMorePages property to false , so that no other page will not be added
e.HasMorePages = False
Else
' if the number of item(per page) is more than 1 then add one page
itemperpage = 1
'initiate itemperpage to 0 .
If totalnumber <= Convert.ToInt32(txtStart.Text) Then
e.HasMorePages = True
End If
'e.HasMorePages raised the PrintPage event once per page .
'It will call PrintPage event again
Return
End If
End While
End Sub
End Class
XAML code
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="175" Width="303">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="1.5*" />
</Grid.ColumnDefinitions>
<Label Content="Start Number:" />
<TextBox x:Name="txtStart" Grid.Column="1" />
<Button Grid.Row="2" Grid.ColumnSpan="2" Content="Print" Click="Button_Click" />
</Grid>
</Window>
The issue is your If statement in the Button_Click method.
If txtStart.Text Then
The If takes a boolean, but you are passing it a string. VB.Net is trying to convert the string to a boolean. 001 works, because it can convert that. J000001 is not able to be converted to a boolean.
Do you mean to check if there is a value entered?
If !String.IsNullOrWhiteSpace(txtStart.Text) Then
You will also run into a similar issue when assigning the value to totalnumber.
You are comparing the value of the text box as a boolean, and then assigning the value of your Text Box to a variable of type Integer.
If txtStart.Text Then
should probably be something like:
If !String.IsNullOrEmpty(txtStart.Text) Then
And then if you job numbers are alpha numeric, use a variable of type String instead of Integer.

Grid.GetRow() always returns 0

I am having trouble getting the row of a WPF grid that a textbox is in.
I have a grid that starts off with one RowDefinition. That row contains an "add" button that adds another rowdefinition to the grid below that row. This new row also contains an "add" button that performs the same function.
The problem I am having is that the function GetRow() always returns 0.
If I declare a button in the XAML that calls the same function, GetRow() returns the correct value. The problem seems to stem from the face that the buttons are created in codebehind.
This is the function that handles the click event of the "add" buttons:
Private Sub btnAddRow_Click(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs)
Dim btnSender As Button = sender
Dim row As Integer
row = Grid.GetRow(btnSender)
AddRow(row)
End Sub
The function "AddRow" adds a new RowDefinition to the grid, the "add" button for that row, and a few other controls (label, textbox, etc).
Private Sub AddRow(ByVal position As Integer)
Dim rd As New RowDefinition()
rd.Height = New GridLength(35, GridUnitType.Pixel)
Me.Height += 35
myGrid.RowDefinitions.Insert(position, rd)
Dim add As New Button
add.Content = "Add Row"
add.HorizontalAlignment = Windows.HorizontalAlignment.Center
add.VerticalAlignment = Windows.VerticalAlignment.Center
AddHandler add.Click, AddressOf btnAddRow_Click
Grid.SetColumn(add, 2)
Grid.SetRow(add, position)
myGrid.Children.Add(add)
End Sub
I found this thread, but using "e.Source" or "e.OriginalSource" did not solve the problem.
Grid.GetRow and Grid.GetColumn keep returning 0
EDIT:
Here is my code. I pulled it out of the project it was in and created a new project for testing.
Class MainWindow
Private Sub btnAddRow_Click(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs)
Dim btnSender As Button = sender
Dim row As Integer
row = Grid.GetRow(btnSender)
row = row + 1
AddRow(row)
End Sub
Private Sub AddRow(ByVal position As Integer)
If (myGrid.RowDefinitions.Count < position) Then
position = myGrid.RowDefinitions.Count
End If
For Each element In (From i As UIElement In myWaypointGrid.Children Where Grid.GetRow(i) >= position Select i).ToList()
Grid.SetRow(element, Grid.GetRow(element) + 1)
Next
Dim rd As New RowDefinition()
rd.Height = New GridLength(35, GridUnitType.Pixel)
Me.Height += 35
myGrid.RowDefinitions.Insert(position, rd)
Dim add As New Button
add.Content = "Add Row " & position
add.HorizontalAlignment = Windows.HorizontalAlignment.Center
add.VerticalAlignment = Windows.VerticalAlignment.Center
AddHandler add.Click, AddressOf btnAddRow_Click
Grid.SetColumn(add, 2)
Grid.SetRow(add, position)
myGrid.Children.Add(add)
End Sub
Private Sub MainWindow_Loaded(ByVal sender As Object, _
ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
AddRow(0)
End Sub
End Class
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid Name="myGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
</Grid.ColumnDefinitions>
</Grid>
Thanks for your help.
Are you ever calling the AddRow function prior to the first "Add" button click? Without more code, it's hard to say why this is not working.
Update to reflect the true issue:
You don't do an increment on the position variable which gets passed into this function so all your buttons are being added to row 0. That is why they all return 0 when you call GetRow

Resources