Windows phone 8 and header with keyboard - c#

So I have a really huge problem:
On my page (It’s about the same as a "messages app" from Microsoft), when I click on my input box and keyboard pop up, my header is moved up and no longer visible.
I’ve searched a little and most of solutions I’ve found were not working (targeting wp7 for them...) . (Like a blog where guy creates a lot of dependency property for then margin of Phoneframe is changed. It works, a little, but the header goes off during animation of keyboard. It’s not enough, it’s really not perfect. )
The Microsoft manages it in standard “Messages” app (With a little bug of font size changing), so it must be possible.
How can realize that ?

I tried this solution and it works just fine :
Try to listen to the TextBox.GotFocus and TextBox.LostFocus events to detect when a TextBox in your application acquires and looses focus.
Put your whole content in a ScrollViewer just as follows :
Code XAML :
<ScrollViewer x:Name="LayoutRoot" Margin="0,0,0,0">
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1">
<TextBox HorizontalAlignment="Left" Height="254" Margin="10,183,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="456" GotFocus="TextBox_GotFocus" LostFocus="TextBox_LostFocus"/>
</Grid>
</Grid>
</ScrollViewer>
Adding the content in a ScrollViewer will give the experience of scrolling even when the keyboard is not open, and that's not really desirable.
For that you need to disable scrolling before the Keyboard is opened and after the keyboard is closed.
In the TextBox_GotFocus event play on the top margin of the ScrollViewer :
in the constructor :
public MainPage()
{
InitializeComponent();
LayoutRoot.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
}
the events :
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
LayoutRoot.Margin = new Thickness(0, 330, 0, 0);
LayoutRoot.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
}
Add the TextBox_LostFocus event handler also to get the page back to its original view when the keyboard is closed :
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
LayoutRoot.Margin = new Thickness(0, 0, 0, 0);
LayoutRoot.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
}
This helps you get the page to its somehow original position when the keyboard is opened.
Hope this helped.

You can make the UI as listbox so that you can scroll the listbox and check the header which is gone up.

When SIP keyboard is rendered, PhoneApplicationFrame.TranslateTransform.Y is set to specific values (-259 in landscape orientation, -339 in portrait orientation). To update layout, we’ll just set top margin to the specified value(-s) and after that Silverlight layout system will fix the issue.
here XAML part:
<Grid x:Name="LayoutRoot" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="WINDOWS PHONE" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="developer's ?" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<Grid Grid.Row="1" Margin="12,0,12,0"></Grid>
<TextBox Grid.Row="2" LostFocus="TextBoxLostFocus"/>
</Grid>
C# portions
private const double LandscapeShift = -259d;
private const double LandscapeShiftWithBar = -328d;
private const double Epsilon = 0.00000001d;
private const double PortraitShift = -339d;
private const double PortraitShiftWithBar = -408d;
public static readonly DependencyProperty TranslateYProperty = DependencyProperty.Register("TranslateY", typeof(double), typeof(MainPage), new PropertyMetadata(0d, OnRenderXPropertyChanged));
public MainPage()
{
InitializeComponent();
Loaded += MainPageLoaded;
}
public double TranslateY
{
get { return (double)GetValue(TranslateYProperty); }
set { SetValue(TranslateYProperty, value); }
}
private static void OnRenderXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MainPage)d).UpdateTopMargin((double)e.NewValue);
}
private void MainPageLoaded(object sender, RoutedEventArgs e)
{
BindToKeyboardFocus();
}
private void BindToKeyboardFocus()
{
PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
if (frame != null)
{
var group = frame.RenderTransform as TransformGroup;
if (group != null)
{
var translate = group.Children[0] as TranslateTransform;
var translateYBinding = new Binding("Y");
translateYBinding.Source = translate;
SetBinding(TranslateYProperty, translateYBinding);
}
}
}
private void UpdateTopMargin(double translateY)
{
if(IsClose(translateY, LandscapeShift) || IsClose(translateY,PortraitShift) || IsClose(translateY, LandscapeShiftWithBar) || IsClose(translateY, PortraitShiftWithBar))
{
LayoutRoot.Margin = new Thickness(0, -translateY, 0, 0);
}
}
private bool IsClose(double a, double b)
{
return Math.Abs(a - b) < Epsilon;
}
private void TextBoxLostFocus(object sender, RoutedEventArgs e)
{
LayoutRoot.Margin = new Thickness();
}
You can try following link. I think it will be helpful.
http://sorokoletov.com/2011/08/windows-phone-70-handling-text-entry-screens/

Related

How to make a control snap to a Grid.Row/Grid.Column in WPF at runtime?

I have a grid with some ColumnDefinitions and RowDefinitions. What I like to do is drag a control at runtime and have it snap to a given GridColumn/GridRow when the control is over that GridColumn/GridRow. I was not able to find any resources on this. Perhaps I am using the wrong key words. Thanks in advance!
You should extend Grid to handle the drop position. Let the Grid add the dropped element to the appropriate cell.
The following simple but working example shows how to enable dragging of any UIElement from a Panel such as StackPanel or Grid to the custom DrockingGrid.
The custom Grid simply overrides the relevant drag&drop overrides. It's a minimal but working example, therefore only OnDragEnter and OnDrop are overridden.
On drop, you basically have to identify the cell the element was dropped in by using the drop position from the DragEventArgs. Then remove the dropped element from its original parent container (where the drag operation has started) and then insert it into the DockingGrid. You then use Grid.Row and Grid.Column to position the element in the appropriate cell:
DockingGrid.cs
public class DockingGrid : Grid
{
private bool AcceptsDrop { get; set; }
private Brush OriginalBackgroundBrush { get; set; }
public DockingGrid()
{
this.AllowDrop = true;
}
protected override void OnDragEnter(DragEventArgs e)
{
base.OnDragEnter(e);
e.Effects = DragDropEffects.None;
this.AcceptsDrop = e.Data.GetDataPresent(typeof(UIElement));
if (this.AcceptsDrop)
{
e.Effects = DragDropEffects.Move;
ShowDropTargetEffects();
}
}
protected override void OnDragLeave(DragEventArgs e)
{
base.OnDragEnter(e);
ClearDropTargetEffects();
}
protected override void OnDrop(DragEventArgs e)
{
base.OnDrop(e);
if (!this.AcceptsDrop)
{
return;
}
ClearDropTargetEffects();
var droppedElement = e.Data.GetData(typeof(UIElement)) as UIElement;
RemoveDroppedElementFromDragSourceContainer(droppedElement);
_ = this.Children.Add(droppedElement);
Point dropPosition = e.GetPosition(this);
SetColumn(droppedElement, dropPosition.X);
SetRow(droppedElement, dropPosition.Y);
}
private void SetRow(UIElement? droppedElement, double verticalOffset)
{
double totalRowHeight = 0;
int targetRowIndex = 0;
foreach (RowDefinition? rowDefinition in this.RowDefinitions)
{
totalRowHeight += rowDefinition.ActualHeight;
if (totalRowHeight >= verticalOffset)
{
Grid.SetRow(droppedElement, targetRowIndex);
break;
}
targetRowIndex++;
}
}
private void SetColumn(UIElement? droppedElement, double horizontalOffset)
{
double totalColumnWidth = 0;
int targetColumntIndex = 0;
foreach (ColumnDefinition? columnDefinition in this.ColumnDefinitions)
{
totalColumnWidth += columnDefinition.ActualWidth;
if (totalColumnWidth >= horizontalOffset)
{
Grid.SetColumn(droppedElement, targetColumntIndex);
break;
}
targetColumntIndex++;
}
}
private void RemoveDroppedElementFromSourceContainer(UIElement droppedElement)
{
DependencyObject parent = droppedElement is FrameworkElement frameworkElement
? frameworkElement.Parent
: VisualTreeHelper.GetParent(droppedElement);
if (parent is null)
{
return;
}
switch (parent)
{
case Panel panel:
panel.Children.Remove(droppedElement);
break;
case ContentControl contentControl:
contentControl.Content = null;
break;
case ContentPresenter contentPresenter:
contentPresenter.Content = null;
droppedElement.UpdateLayout();
break;
case Decorator decorator:
decorator.Child = null;
break;
default:
throw new NotSupportedException($"Parent type {parent.GetType()} not supported");
}
}
private void ShowDropTargetEffects()
{
this.ShowGridLines = true;
this.OriginalBackgroundBrush = this.Background;
this.Background = Brushes.LightBlue;
}
private void ClearDropTargetEffects()
{
this.Background = this.OriginalBackgroundBrush;
this.ShowGridLines = false;
}
}
Usage
Use it like a normal Grid.
Now the user can drag any control into any of the predefined cells.
<local:DockingGrid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="200" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="300" />
<RowDefinition />
</Grid.RowDefinitions>
</local:DockingGrid>
In the parent host of the drag&drop context for example the Window, enable/start the drag behavior:
MainWindow.xaml.cs
partial class MainWindow : Window
{
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
base.OnPreviewMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed
&& e.Source is UIElement uIElement)
{
_ = DragDrop.DoDragDrop(uIElement, new DataObject(typeof(UIElement), uIElement), DragDropEffects.Move);
}
}
}
See Microsoft Docs: Drag and Drop Overview to learn more about the feature.
The short answer is to put that control inside something which fills that cell. You could just put it in that grid cell by adding it to the grid children and setting grid row and column attached properties but there is a gotcha.
A grid cell is sort of conceptual.
The grid looks at it's content, looks at it's definitions for rows and columns and works out where to put it's content using measure arrange passes.
Which is a wordy way of saying there's nothing there to drag your control into.
You need a drop target to drag drop anything into. As it's name suggests, you need some sort of a receptacle for the thing you are dragging.
Wpf, however has these things called content controls.
A button actually inherits from content control to allow it to have things like a string in it.
There is also a content control itself. Which is just kind of like a receptacle for something or other.
One of these things can be used in a given cell as a sort of a place holder. And then you have something in a cell that you can drop into.
I think if you just throw a contentcontrol in a grid without anything inside it you might have problems hit testing.
Some experimentation in a scratch project is advisable.
But basically you could have something like:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Red"
Name="DraggAbleThing"
MouseMove="DraggAbleThing_MouseMove"
/>
<ContentControl Grid.Row="1"
Grid.Column="1"
x:Name="BottomRight"
AllowDrop="True"
>
<Rectangle Fill="Yellow"/>
</ContentControl>
</Grid>
There's a fair bit to implement in order to do drag drop but the idea here is you have something in the bottom right cell which you can drop into. You might have to set ishitestable=false on that yellow rectangle.
I'd have to implement all the drag drop methods to try it out.
If I did and drop works ok then when the contentcontrol gets draggablething dropped into it.
Set the content property of the contentcontrol to draggablething and it is now in the bottom right cell.
It will fill that cell because the grid arranges it's contents to fill whichever logical cell it decides they're "in".
I would like to present an example I wrote that is working.
In the Application I wrote I have a Grid with 4 Rows and 4 Columns.
I can place in each Cell a different UserControl that is based on a class I called
BaseDragDropUserControl:
public class BaseDragDropUserControl: UserControl
{
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed)
{
DataObject data = new DataObject();
data.SetData(DataFormats.StringFormat, nameof(BaseDragDropUserControl));
BaseDragDropUserControl tobemMoved = (BaseDragDropUserControl)e.Source;
int row = (int)tobemMoved.GetValue(Grid.RowProperty);
int col = (int)tobemMoved.GetValue(Grid.ColumnProperty);
data.SetData("Source", tobemMoved);
data.SetData("Row", row);
data.SetData("Col", col);
DragDrop.DoDragDrop(this, data, DragDropEffects.Move);
}
}
protected override void OnGiveFeedback(GiveFeedbackEventArgs e)
{
base.OnGiveFeedback(e);
if (e.Effects.HasFlag(DragDropEffects.Copy))
{
Mouse.SetCursor(Cursors.Cross);
}
else if (e.Effects.HasFlag(DragDropEffects.Move))
{
Mouse.SetCursor(Cursors.Pen);
}
else
{
Mouse.SetCursor(Cursors.No);
}
e.Handled = true;
}
protected override void OnDrop(DragEventArgs e)
{
base.OnDrop(e);
// If the DataObject contains string data, extract it.
if (e.Data.GetDataPresent(DataFormats.StringFormat))
{
string dataString = (string)e.Data.GetData(DataFormats.StringFormat);
if (dataString == nameof(BaseDragDropUserControl))
{
int targetRow = (int)this.GetValue(Grid.RowProperty);
int targetCol = (int)this.GetValue(Grid.ColumnProperty);
int originRow = (int)e.Data.GetData("Row");
int originCol = (int)e.Data.GetData("Col");
BaseDragDropUserControl origin = (BaseDragDropUserControl)e.Data.GetData("Source");
this.SetValue(Grid.RowProperty, originRow);
this.SetValue(Grid.ColumnProperty, originCol);
origin.SetValue(Grid.RowProperty, targetRow);
origin.SetValue(Grid.ColumnProperty, targetCol);
}
}
e.Handled = true;
}
}
The above class is the "Heavy one". It handle both the Drag and the Drop functions.
It ships data object with the origin UserControl and also intercept it when it is dropped. It switch the Grid.Row and Grid.Column values between the origin UserControl and the Target UserControl. In doing this the locations are changed.
I created 2 UsserControls.
RedUserControl and BlueUserControl:
<local:BaseDragDropUserControl x:Class="Problem10.RedUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Problem10"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" AllowDrop="True">
<Grid>
<Rectangle Fill="Red"/>
</Grid>
</local:BaseDragDropUserControl>
<local:BaseDragDropUserControl x:Class="Problem10.BlueUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Problem10"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" AllowDrop="True">
<Grid>
<Rectangle Fill="Blue"/>
</Grid>
</local:BaseDragDropUserControl>
The MainWindow is as following:
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<local:RedUserControl Grid.Row="0" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="0" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="0" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="0" Grid.Column="3"/>
<local:RedUserControl Grid.Row="1" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="1" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="1" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="1" Grid.Column="3"/>
<local:RedUserControl Grid.Row="2" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="2" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="2" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="2" Grid.Column="3"/>
<local:RedUserControl Grid.Row="3" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="3" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="3" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="3" Grid.Column="3"/>
</Grid>
</Window>
The Application is ready for you ! Come and play.

How do I make Xamarin.Forms.iOS View scroll to the Entry that is in focus?

I have a login page with 3 Entry controls under a large image logo and a Button control at the bottom of the page, when I click on the top Entry control to bring the Entry into focus to start typing, the keyboard appears and covers up the Entry controls below and the Login button which is not the best user experience.
The user can only scroll up manually on Xamarin.Forms.iOS but using the same code, ScrollToAsync, on Android I have been able to make the Entry on focus scroll to the top of page by grabbing the parent ScrollView and doing "ScrollView.ScrollToAsync(EntryControl, ScrollToPosition.Start, true);"
On Android, the button at the bottom of the page also moves up by using a * in the Grid Row definition of the empty space in the middle.
It seems like Xamarin.Forms.iOS behaves completely different to Xamarin.Forms.Android when rendering. I have seen the ScrollToAsync has some issues on iOS on page load due to the ScrollView not existing until the page has fully loaded. But this action is on a page that has already fully rendered. What am I doing wrong?
I have tried a Delay as mentioned in this SO, but it doesn't help. ScrollToAsync is not working in Xamarin.Forms
XAML
<Grid>
<ScrollView VerticalOptions="FillAndExpand"
BackgroundColor="White"
x:Name="ScrollViewContainer"
>
<Grid Margin="15, 0, 15, 10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /><!--Logo-->
<RowDefinition Height="Auto" /><!--Entry1-->
<RowDefinition Height="Auto" /><!--Entry2-->
<RowDefinition Height="Auto" /><!--Entry3-->
<RowDefinition Height="*" />
<RowDefinition Height="Auto" /><!--Button-->
<RowDefinition Height="20" /><!--Empty padding-->
</Grid.RowDefinitions>
<Image Grid.Row="0"
Aspect="AspectFit"
Source="CompanyLogo"
HorizontalOptions="Start"
HeightRequest="300"
VerticalOptions="Start"
Margin="10, 20, 20, 0"
/>
<Entry Grid.Row="1"
x:Name="BranchName"
Placeholder="BranchName"
Focused="BranchName_Focused"
/>
<Entry Grid.Row="2"
x:Name="Username"
Placeholder="e.g. Batman "
Focused="Username_Focused"
/>
<Entry Grid.Row="3"
x:Name="Password"
Placeholder="Enter your password"
Focused="Password_Focused"
IsPassword="True"
/>
<Button Grid.Row="5"
x:Name="LoginButton"
Text="Log in"
/>
</Grid>
</ScrollView>
</Grid>
Code Behind
private async void BranchName_Focused(object sender, FocusEventArgs e)
{
await ScrollViewContainer.ScrollToAsync(BranchName, ScrollToPosition.Start, true);
}
The solution was in part by #numan98 answer above which got me re-taking another look at the microsoft docs, https://learn.microsoft.com/en-us/dotnet/api/xamarin.forms.scrollview.scrolltoasync?view=xamarin-forms
It states that ScrollToAsync(double X, double Y, bool animate) will be the final double position values of where X and Y should be.
Thus, X doesn't move anywhere, so I believe it should be 0, and Y I just gave an arbitrary number, such as 250. Somewhere close to the top I believe.
After, we have that iOS bug again where you need to put in a delay.
The final code looks something like this.
private async void BranchName_Focused(object sender, FocusEventArgs e)
{
Device.StartTimer(TimeSpan.FromSeconds(0.25), () =>
{
scroll.ScrollToAsync(0, 250, true);
return false;
);
}
I hope this helps someone. The next thing now is to get the CTA button to move and always be in view.
Add this in your xaml.cs file
protected override void OnAppearing()
{
base.OnAppearing();
userName.Focused += InputFocused;
password.Focused += InputFocused;
userName.Unfocused += InputUnfocused;
password.Unfocused += InputUnfocused;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
userName.Focused -= InputFocused;
password.Focused -= InputFocused;
userName.Unfocused -= InputUnfocused;
password.Unfocused -= InputUnfocused;
}
void InputFocused(object sender, EventArgs args)
{
Content.LayoutTo(new Rectangle(0, -270, Content.Bounds.Width, Content.Bounds.Height));
}
void InputUnfocused(object sender, EventArgs args)
{
Content.LayoutTo(new Rectangle(0, 0, Content.Bounds.Width, Content.Bounds.Height));
}
I think you should try this...
private async void BranchName_Focused(object sender, FocusEventArgs e)
{
await ScrollViewContainer.ScrollToAsync(BranchName.Bounds.X, BranchName.Bounds.Y, true);
}

Transfer a string from MainWindow to a RichTextBox in UserControl

I'm new in WPF, I have a UserControl that called Communication which responsible to connect \ disconnect to a SerialPort class.
Also, there is a LOG which is a RichTextBox and my purpose is to read and write strings which running on the SerialPort buffer, and display them on that log.
The Application looks like that:
Communication XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Orientation="Horizontal"
HorizontalAlignment="Center"
Margin="0,0,0,10">
<TextBlock Text="Choose COM:"
VerticalAlignment="Center"
/>
<ComboBox Name="ComboBoxPorts"
Height="25"
Width="75"
VerticalContentAlignment="Center"
SelectionChanged="ComboBoxPorts_SelectionChanged" />
<Button Name="Button_open_port"
Content="Connect"
Height="25"
Click="open_port_Click"/>
</StackPanel>
<Border BorderThickness="1" BorderBrush="Black" Grid.Row="1">
<ScrollViewer Name="ScrollViewer_LogView"
VerticalScrollBarVisibility="Auto"
Height="220">
<RichTextBox Name="RichTextBox_logView"
Height="220"
VerticalScrollBarVisibility="Visible" />
</ScrollViewer>
</Border>
</Grid>
Also in Code of that UserControl I have function which called "Print to log" that works and print on the LOG the strings that I'm giving her.
Communication CS Code:
public partial class Communication : UserControl
{
public SerialPort mySerialPort = new SerialPort();
public delegate void DataReceivedEventHandler(object sender, DataReceivedEventArgs e);
public event DataReceivedEventHandler DataReceivedEvent;
public Communication()
{
InitializeComponent();
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
private void open_port_Click(object sender, RoutedEventArgs e)
{
// If Close
if (mySerialPort.IsOpen == false)
{
mySerialPort.PortName = ComboBoxPorts.SelectedItem.ToString();
mySerialPort.Open();
Button_open_port.Background = new SolidColorBrush(Colors.Tomato);
Button_open_port.Content = "Disconnect";
}
// If Open
else
{
mySerialPort.Close();
Button_open_port.Background = new SolidColorBrush(Colors.LightGreen);
Button_open_port.Content = "Connect";
}
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
// Print it on the Log
Print_To_Log(indata, System.Windows.Media.Brushes.Red, 0);
}
/// <param name="data">string of the data</param>
/// <param name="color">object "Brushes.xxxxx" -> (xxxxx=name of the color)</param>
/// <param name="direction">1 for TX (sending data), 0 or anything else for RX(receiveing data)</param>
public void Print_To_Log(string data, SolidColorBrush color, int direction = 0)
{
// Print it on the Log
RichTextBox_logView.Dispatcher.BeginInvoke((Action)delegate()
{
TextRange rangeOfTextInput = new TextRange(RichTextBox_logView.Document.ContentEnd, RichTextBox_logView.Document.ContentEnd);
if (direction == 1)
rangeOfTextInput.Text = ">> ";
else
rangeOfTextInput.Text = "<< ";
rangeOfTextInput.ApplyPropertyValue(TextElement.ForegroundProperty, color);
RichTextBox_logView.AppendText(data + "\r");
RichTextBox_logView.ScrollToEnd();
});
}
I have a MainWindow that contains TextBox and Button. Also that MainWindow contains in the XAML the UserControl Communication.
I want be able to write a string on that TextBox, and after clicking the button "Send", transfer the string to the SerialPort (which appears in the UserControl) and it will appears on the LOG.
Also, when I receive something on the SerialPort, it should also appears on that LOG.
How I'm doing that? Please Help.
MainWindow XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<MyProject:Communication Grid.Row="0"
Margin="0,10"
Width="450"
Height="250"/>
<WrapPanel Grid.Row="1">
<TextBox Name="TextBox_input"
Width="200"
HorizontalAlignment="Left"
Margin="50,0,20,0"/>
<Button Name="Button_send"
Width="80"
Content="_Send"
Click="Button_send_Click"/>
</WrapPanel>
</Grid>
MainWindow code:
public MainWindow()
{
InitializeComponent();
}
private void Button_send_Click(object sender, RoutedEventArgs e)
{
}
what you can is you can give Name property to that particular usercontrol which u had added in your mainwindow.xaml and then can get the think
--> Assign Name Property to userControl
By adding x:Name="ucCommunication" to your MyProject:Communication object.
--> Now Make Necessary changes to the UserControl
(Make one Helper Function)
public void GetStringDataFromControl(string content)
{
///write here your required function to execute when u get send button clicked and had textbox text in hand
}
--> Now Pass RichTextbox Text to your UserControl by calling public member function like this
private void Button_send_Click(object sender, RoutedEventArgs e)
{
//here call usecontrol helper function
ucCommunication.GetStringDataFromControl("write the string you want to pass.Here in your case get richtextbox text and convert it to string and pass");
}
In case of any query or concern please let me. If it really helps then please mark it as answer.

Cancel button writes to text box

I have a wpf application that prompts a user to enter some a numeric input for how many seconds to set stale time from a main window. The value is defaulted at 120, if the user wishes to change it he would click on the button and the a new instance of the window would come up and prompt the user to change it, I have the number validation part working, however if the user clicks the cancel button the current value is replaced by a blank(""), empty space, because that is technically the value in enter time window text box. How would I stop this from happening? I just want the window to close the current value to stay what it is. NOTE this also happens if the user enters, say 3, into the box, the default value is replaced with 3 even though the user hit cancel.
Here is the WPF:
<Window x:Class="WpfClient.StaleTimeDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Stale Time" FontFamily="Arial" Width="450" Height="300" Topmost="True" WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Label Margin="10" FontSize="16">Enter new Stale Time</Label>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0, 10, 10, 0">
<TextBox Name="StaleTime" FontSize="20" Width="150" HorizontalAlignment="Left" VerticalContentAlignment="Center" Text="" AcceptsReturn="False" DataContext="{Binding}" PreviewTextInput="StaleTime_PreviewTextInput"></TextBox>
<Label Width="Auto" FontSize="20" Height="Auto" VerticalContentAlignment="Center">Seconds</Label>
</StackPanel>
<StackPanel Grid.Row="3" HorizontalAlignment="Right" VerticalAlignment="Bottom" Orientation="Horizontal">
<Button Name="SaveButton" Margin="10,10,10,10" Style="{StaticResource controlButtonStyle}" IsDefault="True" Click="SaveButton_Click">Save</Button>
<Button Name="CancelButton" Margin="10,10,10,10" Style="{StaticResource controlButtonStyle}" IsCancel="True" Click="CancelButton_Click">Cancel</Button>
</StackPanel>
</Grid>
Here is the C#:
public partial class StaleTimeDialog : Window
{
private Client client = null;
private SystemStatus systemStatus = null;
private UserTypes userType = UserTypes.Unknown;
private bool isStandaloneGUI = false;
private static string CurrentValue = "";
public StaleTimeDialog()
{
InitializeComponent();
CurrentValue = StaleTime.Text;
}
private void Cancel_Click(object sender, EventArgs e)
{
StaleTime.Text = CurrentValue.ToString();
CancelButton.IsCancel = true;
this.Close();
}
private void StaleTime_KeyPress(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
e.Handled = true;
}
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
if (true)
{
this.Close();
}
else
{
ErrorDialog errorDialog = new ErrorDialog(DialogType.OKDialog, IconType.Warning, "Save failed");
errorDialog.ShowDialog();
}
}
private void StaleTime_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
CheckIsNumeric(e);
}
private void CheckIsNumeric(TextCompositionEventArgs e)
{
int result;
if (!(int.TryParse(e.Text, out result) || e.Text == "."))
{
e.Handled = true;
}
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
StaleTime.Undo();
}
}
This is the instance where it is called in C#:
private void EditParametersButton_Click(object sender, RoutedEventArgs e)
{
StaleTimeDialog staletimeDialog = new StaleTimeDialog();
bool? result = staletimeDialog.ShowDialog();
if (staletimeDialog.StaleTime.Text.Equals("") || staletimeDialog.StaleTime.Text.Equals(" "))
{
App.ChipStale = DefaultStaleTime;
}
else
{
App.ChipStale = Convert.ToInt32(staletimeDialog.StaleTime.Text);
}
FoodParametersLine4Run.Text = App.ChipStale.ToString();
}
You need to bind that TextBox Text property in TwoWay mode to a public property supporting change notification in the DataContext of that View (typically a ViewModel) and set the UpdateSourceTrigger to LostFocus or Explicit.
Once you do that all your problems will disappear and as a bonus you'll get rid of all that ugly code-behind... that's the WPF way of doing things.

How to make the canvas detect touch input properly in C#?

Basically, I'm trying to make the canvas listen for a touch input (tap) and will increment the number of taps on screen. It isn't working when I touch the screen on my device. I debugged my code and nothing seems out of the ordinary except that the touch is not detected. I checked ZIndex and the canvas is in front of the screen to be touchable. How do I make it work?
XAML:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" Margin="188,10,187,397"/>
<Button Content="Reset" HorizontalAlignment="Stretch" Margin="-18,535,-18,0" VerticalAlignment="Top" Click="Button_Click"/>
<Canvas ZIndex="0" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeave="Canvas_MouseLeave"/>
</Grid>
C#:
int taps = 0; // create var to detect number of times, user touches the screen
// Constructor
public MainPage()
{
InitializeComponent();
}
// method to register the touch as the finger is placed on the screen
private void Canvas_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//Canvas c = sender as Canvas;
counter.Text = "TOUCHED!";
}
//method register the touch as the finger is lifting up from the screen
private void Canvas_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//Canvas c = sender as Canvas;
taps++;
counter.Text = taps.ToString(); //convert var from int to string
}
//method register the touch as the finger leaves the area of the screen
private void Canvas_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
//Canvas c = sender as Canvas;
MessageBox.Show("You left the screen without lifting your finger. That does not count as a tap!", "Caution!", MessageBoxButton.OK);
}
// method to reset the counter to zero when button is pressed and released
private void Button_Click(object sender, RoutedEventArgs e)
{
taps = 0; // reset the count
counter.Text = taps.ToString(); // convert var from int to string
}
I don't know why you want to do it with Canvas - it won't work as you have nothing in this Canvas, so it can't register your click/tap, Canvas is also hard to adjust to screen. I think it can be done simpler way if you want to do it with MouseUp/Down - subscribe directly to Grid containing your elements instead of filling this Grid with additional Canvas:
In XAML:
<Grid x:Name="ContentPanel" Margin="12,0,12,0" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="7*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" Grid.Row="0"/>
<Button Content="Reset" HorizontalAlignment="Stretch" VerticalAlignment="Center" Click="Button_Click" Grid.Row="1"/>
<TextBlock Name="Touched" FontSize="50" HorizontalAlignment="Center" TextWrapping="Wrap" Text="Touched" VerticalAlignment="Center" Visibility="Collapsed" Grid.Row="2"/>
</Grid>
In code behind:
private int taps = 0;
public MainPage()
{
InitializeComponent();
ContentPanel.MouseLeftButtonDown += ContentPanel_MouseLeftButtonDown;
ContentPanel.MouseLeftButtonUp += ContentPanel_MouseLeftButtonUp;
}
private void ContentPanel_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
taps++;
counter.Text = taps.ToString(); //convert var from int to string
Touched.Visibility = Visibility.Collapsed;
}
private void ContentPanel_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Touched.Visibility = Visibility.Visible;
}
// method to reset the counter to zero when button is pressed and released
private void Button_Click(object sender, RoutedEventArgs e)
{
taps = 0; // reset the count
counter.Text = taps.ToString(); // convert var from int to string
}
As you can see I've subscribed to Grid events (which covers whole screen) - but to make it work I had to set its Background Brush to Transparent, otherwise it will work only if you touch text.
There are many other ways to make your App work, but I hope this will help.
Is there a reason why you don't use the touch-events?
Instead of using MouseLeftButtonDown and MouseLeftButtonUp you should use TouchDown and TouchUp.
Only when you don't handle the touch events or the manipulation events they will be mapped to mouse events. In my experience with touch a single tap also not always gets mapped to MouseLeftButtonDown. As far as I know you could also with mouse events only recoginse one finger. When you want to count more fingers it's necessary to use the TouchDown/TouchUp events.
The problem lies in the overlapping style of the grid
so either make grid rows or define a stackpanel inside the grid, something like this.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<stackpanel>
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" Margin="0,0,0,0"/>
<Canvas ZIndex="0" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeave="Canvas_MouseLeave"/>
<Button Content="Reset" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</stackpanel>
</Grid>
Try and check now.
You should set your Background property. If you don't want any background set it to Transparent:
<Canvas ZIndex="99" Background="Transparent" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" Tapped="Canvas_CountMyTaps"/>
(If you want the canvas to be on Top be sure to make it have a greater ZIndex than the other elements that it overlaps)
If not set (the default value is null) the element won't capture any taps/click etc, it will be as if they "fall through".
Also, consider using the Tapped event which is a "higher level" event that will respond to clicks, taps with the finger, stylus, etc.

Categories

Resources