How disable navigation shortcuts in frame c# WPF - c#

How can I disable the navigation shortcuts in a frame (for example the "Backspace" for navigation backward and "Alt+Right arrow" for navigation forward).
I want to use other keyboard functions, so I want to disable the navigation shortcuts of the frame.
Who can help me?

there is a more elegant solution where Attached behaviours can be used to disable navigation without actually extending a frame.
create an attached-behaviour :
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
namespace A
{
public static class DisableNavigation
{
public static bool GetDisable(DependencyObject o)
{
return (bool)o.GetValue(DisableProperty);
}
public static void SetDisable(DependencyObject o, bool value)
{
o.SetValue(DisableProperty, value);
}
public static readonly DependencyProperty DisableProperty =
DependencyProperty.RegisterAttached("Disable", typeof(bool), typeof(DisableNavigation),
new PropertyMetadata(false, DisableChanged));
public static void DisableChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var frame = (Frame)sender;
frame.Navigated += DontNavigate;
frame.NavigationUIVisibility = NavigationUIVisibility.Hidden;
}
public static void DontNavigate(object sender, NavigationEventArgs e)
{
((Frame)sender).NavigationService.RemoveBackEntry();
}
}
}
And in the xaml add this whenever you use a frame :
<Frame beha:DisableNavigation.Disable="True" />
and at the top of the xaml add the import :
xmlns:beha="clr-namespace:A"

See this answer for how to disable the keyboard shortcuts:
Disable backspace in wpf
That doesn't work for the back and forward navigation mouse buttons. To prevent that, it seems you need to put a handler on the Navigating event and cancel it if you don't want it.
For example, to totally disable forward navigation:
In .xaml:
<Frame Navigating="HandleNavigating" />
In code behind:
void HandleNavigating(Object sender, NavigatingCancelEventArgs e)
{
if (e.NavigationMode == NavigationMode.Forward)
{
e.Cancel = true;
}
}

What I do is host the content in ContentControl.

The real answer to disable all shortcuts in WPF Frame is:
foreach (var vNavigationCommand in new RoutedUICommand[]
{ NavigationCommands.BrowseBack,
NavigationCommands.BrowseForward,
NavigationCommands.BrowseHome,
NavigationCommands.BrowseStop,
NavigationCommands.Refresh,
NavigationCommands.Favorites,
NavigationCommands.Search,
NavigationCommands.IncreaseZoom,
NavigationCommands.DecreaseZoom,
NavigationCommands.Zoom,
NavigationCommands.NextPage,
NavigationCommands.PreviousPage,
NavigationCommands.FirstPage,
NavigationCommands.LastPage,
NavigationCommands.GoToPage,
NavigationCommands.NavigateJournal })
{
ctlFrame.CommandBindings.Add(new CommandBinding(vNavigationCommand, (sender, args) => { }));
}

The frame it's self provides no method of disabling navigation. It only provides a means to hide the navigation controls. You can however inherit from the Frame class and make some modifications to it yourself. The following example removes the last page from the BackStack every time the page navigates. Thus ensuring the frame can never navigate backwards as it does not know which page was last.
class NoNavFrame : Frame
{
public NoNavFrame()
{
this.Navigated += new System.Windows.Navigation.NavigatedEventHandler(NoNavFrame_Navigated);
}
void NoNavFrame_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
this.NavigationService.RemoveBackEntry();
}
}
Then you can include this in XAML as follows...
<myControls:NoNavFrame x:Name="myFrame" NavigationUIVisibility="Hidden" />

Related

detecting air-tap event in an 2D UWP application for Hololens 1

I am trying to detect an air-tap event in my 2D UWP application for Hololens 1. I am using VS 2019 for my development. I have followed some sample code from BasicHologram and Hands and motion controllers in DirectX. Here is my sample code I wrote:
SpatialInputHandler.cs
public class SpatialInputHandler
{
private SpatialInteractionManager interactionManager;
private SpatialInteractionSourceState sourceState;
public SpatialInputHandler()
{
interactionManager = SpatialInteractionManager.GetForCurrentView();
interactionManager.SourcePressed += this.OnSourcePressed;
}
public SpatialInteractionSourceState CheckForInput()
{
SpatialInteractionSourceState sourceState = this.sourceState;
this.sourceState = null;
return sourceState;
}
public void OnSourcePressed(SpatialInteractionManager sender, SpatialInteractionSourceEventArgs args)
{
sourceState = args.State;
}
}
MainPage.cs
public sealed partial class MainPage : Page
{
private SpatialInputHandler spatialInputHandler;
DispatcherTimer dispatcherTimer;
public MainPage()
{
this.InitializeComponent();
spatialInputHandler = new SpatialInputHandler();
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(60);
dispatcherTimer.Tick += DispatcherTimer_Tick;
dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, object e)
{
SpatialInteractionSourceState pointerState = spatialInputHandler.CheckForInput();
if (pointerState != null && pointerState.IsPressed)
{
textBlock2.Text = "airtap detected";
}
if(pointerState == null)
{
textBlock2.Text = "no airtap detected";
}
}
}
In my code I am always getting pointerState value as null. Could someone please help me how to figure this out. Thanks!
Because you are develeping a 2d flat app on HoloLens, the SpatialInteractionManager class is not designed for such scenario, it's for holographic apps.
For a flat app, it will run in the shell and as such we would need to handle routed events which are for input and user interaction scenarios(e.g. touch, mouse, etc…). For example, your UI might have a button(Button inherited from the UIElement base class) and handle the Click or Tap event:
XAML:
<Grid>
<Button Name="btnClick" Content="Click" Width="100" Height="50" Click="BtnClick_Click" Tapped="BtnClick_Tapped" />
</Grid>
Backend:
private void BtnClick_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("Clicked");
}
private void BtnClick_Tapped(object sender, TappedRoutedEventArgs e)
{
Debug.WriteLine("Tapped");
}
These events can respond correctly with your air-tap interaction on HoloLens.
For more details, check our this documentation: Events and routed events overview
If you can share more details about how you want the air tap to interact with your app, we're happy to help you to move forward.

Adding controls to a panel on a User Control in designer

I have a specific requirement to create a user control with specific common functions. To that control I also have the requirement to allow other developers to add controls in designer mode to make specific UI's. To do this I created a user control, adding (sample) label, and button. I also added a panel to allow adding of addition controls in a specific area of the control.
I then made the made the class visible in designer mode by adding [Designer] markup and a [ControlDesigner]. This gives the desired effect to add a User control with some fixed content, and add more controls to the page. The problem is that the panel can be moved by the user in design mode, and VisualStudio gets confused, creating a circular reference.. I must be missing something? Can I turn off the resizing/positioning of the panel, even though I need design mode enabled?
NOTE: I also tried to just use a user control in design mode, but added controls keep disappearing behind the fixed controls on the User Control.
Code and examples are below.. Any suggestion/fixes welcomed..
Above is the visual of the user control with the panel
Above is a form including the User control, and adding a custom button to the panel.. Note the panel drag is enable, if touched, a circular reference gets created in the form.designer.cs file, and the project becomes unstable.
Finally below is the class for User Control
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using System.Windows.Forms.Design;
namespace wfcLib
{
[DesignerAttribute(typeof(MyControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public partial class ucInput : UserControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel InternalPanel
{
get { return pnlContent; }
set { pnlContent = value; }
}
public ucInput()
{
InitializeComponent();
}
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class MyControlDesigner : System.Windows.Forms.Design.ControlDesigner
{
public override void Initialize(IComponent c)
{
base.Initialize(c);
ucInput ctl = (ucInput)c;
EnableDesignMode(ctl.InternalPanel, "InternalPanel");
}
}
}
In addition to my comment concerning using a derived Panel with its own designer that overrides the SelectionRules property, another method would be to tap into the designer's ISelectionService to detect a change in selected components and remove the panel if it was selected.
This is accomplished by overriding the control's Site property to set the hook. Also note that I changed the InternalPanel property to be read-only as you really do not want that writable.
[DesignerAttribute(typeof(MyControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public partial class ucInput : UserControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel InternalPanel
{
get { return pnlContent; }
}
public ucInput()
{
InitializeComponent();
}
private ISelectionService selectionService;
private IDesignerHost host;
public override ISite Site
{
get
{
return base.Site;
}
set
{
host = null;
UnSubscribeFromSelectionService();
base.Site = value;
if (value != null)
{
host = (IDesignerHost)this.Site.GetService(typeof(IDesignerHost));
if (host != null)
{
if (host.Loading)
{
// defer subscription to selection service until fully loaded
host.Activated += Host_Activated;
}
else
{
SubscribeToSelectionService();
}
}
}
}
}
private void Host_Activated(object sender, EventArgs e)
{
host.Activated -= Host_Activated;
SubscribeToSelectionService();
}
private void SubscribeToSelectionService()
{
selectionService = (ISelectionService)this.Site.GetService(typeof(ISelectionService));
if (selectionService != null)
{
selectionService.SelectionChanging += OnSelectionChanging;
}
}
private void UnSubscribeFromSelectionService()
{
if (selectionService != null)
{
selectionService.SelectionChanging -= OnSelectionChanging;
}
}
private void OnSelectionChanging(object sender, EventArgs e)
{
if (selectionService.GetComponentSelected(pnlContent))
{
selectionService.SelectionChanging -= OnSelectionChanging;
selectionService.SetSelectedComponents(new[] { pnlContent }, SelectionTypes.Remove);
selectionService.SelectionChanging += OnSelectionChanging;
}
}
}
Edit: The original code neglected to account for SelectionService not being available while the IDesignerHost is loading. Added code to defer subscription until the IDesignerHost is activated.

Xamarin.Forms using PanGestureRecognizer - move outside of Window area on UWP does not trigger Status Canceled or Completed

My question is regarding the PanGestureRecognizer on Xamarin.Forms, specifically on UWP. The pan gesture works great and the menu slides in and out but if you move the mouse cursor outside of the application window it does not trigger GestureStatus.Canceled, or GestureStatus.Completed. I've simplified the code below because it is not important. The pan gesture is working correctly and the menu is sliding in and out with the pan. The issue is it is not triggering anything if you move the cursor outside of the app window.
Code below:
panContainer is a simple ContentView.
var panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += PanUpdated;
panContainer.GestureRecognizers.Add(panGesture);
private void PanUpdated(object sender,PanUpdatedEventArgs e) {
switch(e.StatusType) {
case GestureStatus.Started:
break;
case GestureStatus.Running:
menuLayout.TranslationX = (float)e.TotalX;
break;
case GestureStatus.Completed:
case GestureStatus.Canceled: //I assumed this would be called!
if(menuLayout.TranslationX < 100) {
Close();
} else {
Open();
}
break;
}
}
Thanks in advance!
if you move the mouse cursor outside of the application window it does not trigger GestureStatus.Canceled, or GestureStatus.Completed. I've simplified the code below because it is not important. The pan gesture is working correctly and the menu is sliding in and out with the pan. The issue is it is not triggering anything if you move the cursor outside of the app window.
I have tested your code and reproduce the issue. I tried to find the cause of the problem in the source code. I found the following code.
void OnPointerCanceled(object sender, PointerRoutedEventArgs e)
{
uint id = e.Pointer.PointerId;
if (_fingers.Contains(id))
_fingers.Remove(id);
PinchComplete(false);
PanComplete(false);
}
void OnPointerExited(object sender, PointerRoutedEventArgs e)
{
uint id = e.Pointer.PointerId;
if (_fingers.Contains(id))
_fingers.Remove(id);
PinchComplete(true);
PanComplete(true);
}
void OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
uint id = e.Pointer.PointerId;
if (!_fingers.Contains(id))
_fingers.Add(id);
}
void OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
uint id = e.Pointer.PointerId;
if (_fingers.Contains(id))
_fingers.Remove(id);
PinchComplete(true);
PanComplete(true);
}
The PanComplete method will be invoked when you PointerExited,ManipulationCompleted etc. But it has not been invoked when the pointer exited. And then I tried to test PointerExited and ManipulationCompleted event in uwp native project. Both are working properly.
<Border Width="200" Height="200" Background="Red" PointerEntered="Border_PointerEntered" PointerExited="Border_PointerExited" ManipulationCompleted="Border_ManipulationCompleted"/>
So,there may be some issues with xamarin.
Workable solution based on the previous comment
//Shared part
public class PanOutsideWindowEffect : RoutingEffect
{
public event EventHandler<PanUpdatedEventArgs> PanUpdated;
public PanOutsideWindowEffect() : base($"{ResolutionGroupName.Organization}.{nameof(PanOutsideWindowEffect)}")
{
}
public void SendPanCancel()
{
PanUpdated?.Invoke(this, new PanUpdatedEventArgs(GestureStatus.Canceled, 0));
}
}
//UWP part
public class PanOutsideWindowEffect : PlatformEffect
{
private FrameworkElement frameworkElement;
private MobileApp.Effects.PanOutsideWindowEffect effect;
protected override void OnAttached()
{
frameworkElement = Control == null ? Container : Control;
effect = (MobileApp.Effects.PanOutsideWindowEffect)Element.Effects.First(e => e is MobileApp.Effects.PanOutsideWindowEffect);
if (frameworkElement != null)
{
frameworkElement.PointerExited += OnPointerExited;
}
}
protected override void OnDetached()
{
if (frameworkElement != null)
{
frameworkElement.PointerExited -= OnPointerExited;
}
}
private void OnPointerExited(object sender, PointerRoutedEventArgs args)
{
effect?.SendPanCancel();
}
}

WPF Chatbox control

currently I'm working on a chat client and changed my client from windows forms (as forms is shitty) to WPF. I'm not really sure which control could be used to realize a chatbox. I could take a TextBox but this will not display the complete content when it's full. I also tried to use a ListBox but when I try to add items they are not displayed. I used this code to add content to it:
internal void AddMessage(string message)
{
listBox_messages.Items.Add(message);
listBox_messages.Items.Refresh();
}
Does anybody knows which control would be the best for this purpose?
Thanks for your help!
Edit:
I implemented a TextBox for this and disabled it. But the text I'm appending by this method is not shown.
My class:
using System.ComponentModel;
using System.Windows;
namespace Chat_Client
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
/// <summary>
/// MainWindow constructor
/// </summary>
public MainWindow()
{
InitializeComponent();
textBox_messages.AppendText("Test" + "\n");
textBox_messages.AppendText("Test" + "\n");
textBox_messages.AppendText("Test" + "\n");
Closing += OnWindowClosing;
}
private void OnWindowClosing(object sender, CancelEventArgs e)
{
Program.Shutdown();
}
private void button_connect_Click(object sender, RoutedEventArgs e)
{
if(Program.Connected)
{
Program.Disconnect();
}
else
{
Program.Connect();
}
}
private void button_sendMessage_Click(object sender, RoutedEventArgs e)
{
}
internal void AddMessage(string message)
{
textBox_messages.AppendText(message + "\n");
}
}
}
The test strings are shown but the text added by the method AddMessage is not. I can verify that the method is called, I just checked it with a breakpoint inside this method. Anybody has a clue how this could happen?
Update
If the Program class calls AddMessage(string) from within another thread than the UI thread, you have to use a Dispatcher in order to update the UI.
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
mainWindow.AddMessage(message);
}));
MVVM is the way to go when using WPF.
Add your messages to an ObservableCollection in your ViewModel class (ChatViewModel.cs):
public class ChatViewModel
{
public ObservableCollection<string> Messages { get; } = new ObservableCollection<string>();
internal void AddMessage(string message)
{
Messages.Add(message);
}
}
Set this ViewModel as the DataContext of your View (ChatView.xaml)
public ChatView : UserControl
{
public ChatView()
{
InitializeComponent();
DataContext = new ChatViewModel();
}
}
In the XAML-Code bind the ObservableCollection to the ItemsSource property of your ListBox:
<ListBox ItemsSource="{Binding Messages}") />
When you add a message to the Messages collection it should appear inside the ListBox. This is not a complete example, but it should guide you into the right direction.
*In response to your edit: where are you calling AddMessage() from? I tried your code and just called AddMessage("foo"); from the button click event and worked fine.
For a chat box, I would use a TextBox to write the chat messages, and wrap it with a ScrollViewer for scrolling. After using AppendText() on the TextBox to write the message, you can then call ScrollToEnd() to scroll to the bottom of the TextBox.
In the XAML:
<ScrollViewer x:Name="ScrollViewer" ScrollChanged="ScrollViewer_OnScrollChanged">
<TextBox x:Name="ChatBox"/>
</ScrollViewer>
In the code behind:
private void WriteToChat(string message)
{
ChatBox.AppendText(message);
ChatBox.ScrollToEnd();
}
private bool _autoScroll = true;
private void ScrollViewer_OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.ExtentHeightChange == 0)
{
_autoScroll = ScrollViewer.VerticalOffset == ScrollViewer.ScrollableHeight;
}
if (_autoScroll && e.ExtentHeightChange != 0)
{
ScrollViewer.ScrollToVerticalOffset(ScrollViewer.ExtentHeight);
}
}
You can use a TextBox and set the VerticalAlignment to Stretch in your Xaml file.
< TextBox x:Name="textbox" VerticalAlignment="Stretch">

How to override the onTouchUp in the user control which can be used to fire the event not only on this user control, but whole application

I am a new to the C# and WPF. Right now, I want to override onTouchUp event in my user control. Then I can add user control in the references for reuse. The problem is each time I want to test this event on the application, the event only fires at area of user control, not whole screen. Does anyone have a solution for this?
Basically, you need to attach to PreviewTouchUp event on a element, that covers the interactive area. It may be application wndows as well. Handling mouse or touch events outside window is not recommended.
Here's an example, how can you attach any behaviour to any element directly in xaml:
<Window my:MyBehaviour.DoSomethingWhenTouched="true" x:Class="MyProject.MainWindow">
public static class MyBehaviour
{
public static readonly DependencyProperty DoSomethingWhenTouchedProperty = DependencyProperty.RegisterAttached("DoSomethingWhenTouched", typeof(bool), typeof(MyBehaviour),
new FrameworkPropertyMetadata( DoSomethingWhenTouched_PropertyChanged));
private static void DoSomethingWhenTouched_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uiElement = (UIElement)d;
//unsibscribe firt to avoid multiple subscription
uiElement.PreviewTouchUp -=uiElement_PreviewTouchUp;
if ((bool)e.NewValue){
uiElement.PreviewTouchUp +=uiElement_PreviewTouchUp;
}
}
static void uiElement_PreviewTouchUp(object sender, System.Windows.Input.TouchEventArgs e)
{
//you logic goes here
}
//methods required by wpf conventions
public static bool GetDoSomethingWhenTouched(UIElement obj)
{
return (bool)obj.GetValue(DoSomethingWhenTouchedProperty);
}
public static void SetDoSomethingWhenTouched(UIElement obj, bool value)
{
obj.SetValue(DoSomethingWhenTouchedProperty, value);
}
}

Categories

Resources