I making a prompter, white text on black background scrolling to end. The errors received do not break the program, but they are suspicious.
I got:
Warning RichTextBox,
Name='RichPrompterTextBox' Text.Length TextBlock.Text,
Name='CharacterCounterTextBlock' String Text property not found on
object of type RichTextBox. Warning RichTextBox,
Name='RichPrompterTextBox' MaxLength TextBlock.Text,
Name='CharacterCounterTextBlock' String MaxLength property not found
on object of type RichTextBox. Error null (0) TextBlock.Visibility,
Name='CharacterCounterTextBlock' Visibility Cannot find source:
RelativeSource FindAncestor,
AncestorType='System.Windows.Controls.TextBox', AncestorLevel='1'.
I have this XAML:
<Window x:Class="WpfDrawing.Prompter.PrompterView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfDrawing.View"
xmlns:prompter="clr-namespace:WpfDrawing.Prompter" d:DataContext="{d:DesignInstance Type=prompter:PrompterViewModel}"
mc:Ignorable="d"
WindowState="Maximized"
WindowStyle="None"
Title="Prompter" Height="450" Width="800">
<RichTextBox
Background="Black"
Name ="prompterRichTextBox"
Foreground="White">
</RichTextBox>
</Window>
and this .cs:
/// <summary>
/// Interaction logic for Prompter.xaml
/// </summary>
public partial class PrompterView : Window
{
private readonly PrompterViewModel vm;
public PrompterView(PrompterViewModel vm)
{
this.vm = vm;
DataContext = vm;
InitializeComponent();
this.vm.PropertyChanged += Vm_PropertyChanged;
this.RichPrompterTextBox.MouseWheel += RichPrompterTextBox_MouseWheel;
this.RichPrompterTextBox.LayoutUpdated += RichPrompterTextBox_LayoutUpdated;
this.RichPrompterTextBox.TextChanged += RichPrompterTextBox_TextChanged;
SetToAdditionalMonitor();
}
private void SetToAdditionalMonitor()
{
var screens = Screen.AllScreens;
for (var i = 0; i != screens.Length; i++)
{
if (!screens[i].Primary)
{
this.WindowState = WindowState.Normal;
this.WindowStartupLocation = WindowStartupLocation.Manual;
this.Left = screens[i].Bounds.Left;
this.Top = screens[i].Bounds.Top;
this.Width = screens[i].Bounds.Width;
this.Height = screens[i].Bounds.Height;
}
}
}
private void RichPrompterTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var textRange = new TextRange(this.RichPrompterTextBox.Document.ContentStart, this.RichPrompterTextBox.Document.ContentEnd);
vm.Text = textRange.Text;
}
private void RichPrompterTextBox_LayoutUpdated(object sender, System.EventArgs e)
{
vm.Height = this.RichPrompterTextBox.ViewportHeight + this.RichPrompterTextBox.ExtentHeight;
}
private void RichPrompterTextBox_MouseWheel(object sender, MouseWheelEventArgs e)
{
vm.ScrollPosition = RichPrompterTextBox.VerticalOffset;
}
private void Vm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(vm.ScrollPosition))
{
this.RichPrompterTextBox.Dispatcher.Invoke(() => this.RichPrompterTextBox.ScrollToVerticalOffset(vm.ScrollPosition));
}
if(e.PropertyName == nameof(vm.Text))
{
var textRange = new TextRange(this.RichPrompterTextBox.Document.ContentStart, this.RichPrompterTextBox.Document.ContentEnd);
if (textRange.Text != vm.Text)
{
this.RichPrompterTextBox.Document = new FlowDocument(new Paragraph(new Run(vm.Text)));
}
}
}
private void Reset(object sender, RoutedEventArgs e)
{
vm.Reset();
}
private void StartStop(object sender, RoutedEventArgs e)
{
vm.RunStop();
}
private void CloseWindow(object sender, RoutedEventArgs e) => Close();
}
Xaml errors:
errors
I didn't bind anything, but I get binding errors.
These errors are not related to the code you've posted.
In your program you have bindings because these errors are indeed binding errors, where it seems that:
you're binding the Text property of the instance CharacterCounterTextBlock (of type TextBlock ) to a property named Text.Length of an instance of type RichTextBox named prompterRichTextBox. But the type RichTextBox doesn't have that propery.
you're binding the Text property of the instance CharacterCounterTextBlock (of type TextBlock ) to a property named MaxLength of an instance of type RichTextBox named prompterRichTextBox. But the type RichTextBox doesn't have that propery.
you're binding the Visibility property of the instance CharacterCounterTextBlock (of type TextBlock ) to something that's null because is not found in the source declaration.
More generally speaking, you shouldn't ask questions to solve specific issues related to bugs in your code, but if it's the case, please try to replicate the issue in a GitHub project (or similar public repositories), so that it's easier to understand the code you've used and find the cause.
Related
I'm stuck with wpf commands and "non-trivial" hotkeys. I want to map "+" key to some command. Within i want it to keep working with any textbox. Below is the sample
Commands.cs
public static class Commands
{
private static readonly ICommand _someCommand;
static Commands()
{
_someCommand = new RoutedCommand("cmd", typeof(Commands), new InputGestureCollection { new KeyGesture(Key.OemPlus), new KeyGesture(Key.Add) });
}
public static ICommand SomeCommand
{
get { return _someCommand; }
}
}
MainWindow.xaml
<Window x:Class="WpfHotkeysTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wpfHotkeysTest="clr-namespace:WpfHotkeysTest"
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding Command="wpfHotkeysTest:Commands.SomeCommand" Executed="CommandBinding_OnExecuted"></CommandBinding>
</Window.CommandBindings>
<TextBox></TextBox>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void CommandBinding_OnExecuted(object sender, ExecutedRoutedEventArgs e)
{
Debug.WriteLine("COMMAND! " + e.Source);
}
}
The problem is when im focused on the textbox it does not handle pressed "+" key before command is executed.
I want my key be displayed, but how do i achieve this in the best way?
UPD
I dont want to execute command if the key was handled by the text box.
I know there is property CanExecuteRoutingEventArgs.ContinueRouting. But it executes both command and textbox handling
The workaround I've found so far
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void CommandBinding_OnExecuted(object sender, ExecutedRoutedEventArgs e)
{
Debug.WriteLine("COMMAND! " + e.Source);
}
private void CommandBinding_OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (IsEditableControlSelected())
{
e.ContinueRouting = true;
return;
}
e.CanExecute = true;
}
private bool IsEditableControlSelected()
{
return Keyboard.FocusedElement is TextBox;
}
}
This brings tears to my eyes, but at least this is better than nothing. Waiting for more solutions
I am having a master window in which there are plenty of user control. and using navigation i am able to access the user controls. But by question is how to set focus on the first text box when ever the user control is opened.
I tried with dependency property and boolean flags, i was able to succeeded a bit. When i first render the UserControl i was able to focus but when i open for the second time i was not able to set focus on the TextBox.
And one more thing, i have validation for TextBoxes, if the validation fails then the textbox should be emptied and the focus should be on the respective text box.
How can i achieve this using MVVM in WPF (CLR 3.5, VS2008)
thanks in advance.
If you have a UserControl then you also have CodeBehind.
Place this inside your codebehind and you will do fine.
this.Loaded += (o, e) => { Keyboard.Focus(textBox1) }
Place this inside your UserControl XAML if you wish to listen to validation errors.
<UserControl>
<Grid Validation.Error="OnValidationError">
<TextBox Text{Binding ..., NotifyOnValidationError=true } />
</Grid>
<UserControl>
Inside the CodeBehind of your UserControl you will have something like this:
public void OnValidationError(o , args)
{
if(o is TextBox)
{
(TextBox)o).Text = string.Empty;
}
}
You should use AttachedProperty to stick to MVVM pattern it'll keep your view model independent of UI code and fully unit testable. Following attached property binds a boolean property to focus and highlight the TextBox, if you do not want the highlighting then you can remove the highlighting code and just work with focus code.
public class TextBoxBehaviors
{
#region HighlightTextOnFocus Property
public static readonly DependencyProperty HighlightTextOnFocusProperty =
DependencyProperty.RegisterAttached("HighlightTextOnFocus", typeof (bool), typeof (TextBoxBehaviors),
new PropertyMetadata(false, HighlightTextOnFocusPropertyChanged));
public static bool GetHighlightTextOnFocus(DependencyObject obj)
{
return (bool) obj.GetValue(HighlightTextOnFocusProperty);
}
public static void SetHighlightTextOnFocus(DependencyObject obj, bool value)
{
obj.SetValue(HighlightTextOnFocusProperty, value);
}
private static void HighlightTextOnFocusPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
var uie = sender as UIElement;
if (uie == null) return;
if ((bool) e.NewValue)
{
uie.GotKeyboardFocus += OnKeyboardFocusSelectText;
uie.PreviewMouseLeftButtonDown += OnMouseLeftButtonDownSetFocus;
}
else
{
uie.GotKeyboardFocus -= OnKeyboardFocusSelectText;
uie.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDownSetFocus;
}
}
private static void OnKeyboardFocusSelectText(object sender, KeyboardFocusChangedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null) return;
textBox.SelectAll();
}
private static void OnMouseLeftButtonDownSetFocus(object sender, MouseButtonEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null) return;
if (!textBox.IsKeyboardFocusWithin)
{
textBox.Focus();
e.Handled = true;
}
}
#endregion
}
You can use this attached property in on your TextBox which you want to focus/highlight...
<TextBox ... local:TextBoxBehaviors.HighlightTextOnFocus="{Binding IsScrolledToEnd}" ... />
You can also try using FocusManager
<UserControl>
<Grid FocusManager.FocusedElement="{Binding Path=FocusedTextBox, ElementName=UserControlName}">
<TextBox x:Name="FocusedTextBox" />
</Grid>
<UserControl>
I've seen answers to the question of how to break out of a while loop with a keypress for a console app and a winforms app but not a WPF app. So, uh, how do you do it? Thanks.
Okay, let's elaborate:
Something like this doesn't work in a WPF (non-console) app. It throws a runtime error:
while(!Console.KeyAvailable)
{
//do work
}
MainWindow.xaml:
<Window x:Class="WpfApplication34.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350">
<Grid>
<TextBlock x:Name="tb" />
</Grid>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow:Window {
private int _someVal = 0;
private readonly CancellationTokenSource cts = new CancellationTokenSource();
public MainWindow() {
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object sender, RoutedEventArgs routedEventArgs) {
KeyDown += OnKeyDown;
while (!cts.IsCancellationRequested) {
await Task.Delay(1000); // Some Long Task
tb.Text = (++_someVal).ToString();
}
}
private void OnKeyDown(object sender, KeyEventArgs keyEventArgs) {
if (keyEventArgs.Key == Key.A)
cts.Cancel();
}
}
It's just a rough demo, just take the concept. The only thing specific to WPF here is the manner of capturing the key-press. Everything else relating to breaking the while loop is the same across a console app or wpf or winforms.
You can create na event in the KeyDown Event in the MainWindow and get the KeyEventArgs e to know what key was pressed.
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.A)
{
// set a flag to break the loop
}
}
I have the following problem: I have a TextBox in WPF application. When I type in a text that is very long (more characters than what can display in the textbox field)
and than move away from that textbox field (to some other textbox, for example), the text I just typed in, stays right-justified (where I left it).
In other words, I cannot again see the beginning of the text unless I hit Home key or close the screen and open it again.
Can I align the text to the left after I move to the other textbox on the window. I tried with a most probably "fish" solution and it does not work:
private void TextEditControl_LostFocus(object sender, RoutedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox != null)
{
textBox.Dispatcher.BeginInvoke(
DispatcherPriority.Send,
new Action(() => SendKeys.SendWait("{HOME}")));
}
}
Try this:
textBox.SelectionStart = 0;
As per Meleak's note on the Tim Dams' answer, here's how you do it as an attached behavior:
using System.Windows;
using System.Windows.Controls;
public static class TextBoxBehavior
{
public static bool GetHomeOnLostFocus(DependencyObject obj)
{
return (bool)obj.GetValue(HomeOnLostFocusProperty);
}
public static void SetHomeOnLostFocus(DependencyObject obj, bool value)
{
obj.SetValue(HomeOnLostFocusProperty, value);
}
// Using a DependencyProperty as the backing store for HomeOnLostFocus.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty HomeOnLostFocusProperty =
DependencyProperty.RegisterAttached(
"HomeOnLostFocus",
typeof(bool),
typeof(TextBoxBehavior),
new UIPropertyMetadata(false, OnHomeOnLostFocusChanged));
public static void OnHomeOnLostFocusChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
// Type checking and casting of parameters
bool oldVal = (bool)e.OldValue;
bool newVal = (bool)e.NewValue;
TextBox textBox = d as TextBox;
// Argument value tests
if (textBox == null) return;
if (oldVal == newVal) return;
// If HomeOnLostFocus then add event handler, otherwise, remove it.
if (newVal)
textBox.LostFocus += TextBox_LostFocus;
else
textBox.LostFocus -= TextBox_LostFocus;
}
static void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
var textBox = (TextBox)sender;
textBox.SelectionStart = 0;
}
}
Need references to PresentationCore, PresentationFramework, System.Xaml and WindowsBase assemblies.
Here's the usage example:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tbb="clr-namespace:TextBoxBehavior;assembly=TextBoxBehavior"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBox Width="200"/>
<TextBox tbb:TextBoxBehavior.HomeOnLostFocus="true" Width="200"/>
<Button Content="Dummy" Width="200"/>
</StackPanel>
</Window>
Note the xmlns:tbb attribute and its usage on the 2nd TextBox.
Working on a TouchScreen application which also has a keyboard attached, I have the following problem:
The WPF window has a TextBox, which should receive ALL keyboard input. There are also Buttons and a ListBox, which are solely used by the TouchScreen(=Mouse).
A very simple example looks like this:
<Window x:Class="KeyboardFocusTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<StackPanel>
<TextBox Text="{Binding Input, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
PreviewLostKeyboardFocus="TextBox_PreviewLostKeyboardFocus"/>
<Button Click="Button_Click">Add</Button>
<ListBox ItemsSource="{Binding Strings}" />
</StackPanel>
</Window>
To keep the TextBox always focused, I just do:
private void TextBox_PreviewLostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
{
e.Handled = true;
}
So far so good - the problem now is, that I can't select items from the ListBox anymore. This only seems to work, if the ListBox has the keyboard focus. But if I loose the keyboard focus on the TextBox, I can't enter text anymore without clicking it first.
Any ideas, comments suggestions are welcome!
There might be a more elegant solution for this, but you could always handle the PreviewKeyDown event at the Window level, and pass focus to the TextBox if it doesn't already have it, instead of preventing it from losing focus in the first place. That way, the ListBox can use focus as is normal, but as soon as a key is pressed focus jumps right to the TextBox. In addition, you can filter out keys that you don't want to switch focus - the arrow keys come to mind, which could then be used to move up and down in the ListBox.
Adding an event handler like the following should do the trick:
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (!textBox.IsFocused)
{
textBox.Focus();
}
}
Based on Nicholas' suggestion (thx!), here's a markup extension, which is used like:
<TextBox Helpers:KeyboardFocusAttractor.IsAttracted="true" />
It seems to work, and ANTS didn't show any memory leaks. But when it comes to WPF and especially events and bindings, you never know, so use with care!
public static class KeyboardFocusAttractor
{
public static readonly DependencyProperty IsAttracted = DependencyProperty.RegisterAttached("IsAttracted",
typeof (bool), typeof (KeyboardFocusAttractor), new PropertyMetadata(false, OnIsAttracted));
private static void OnIsAttracted(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var isAttracted = (bool) e.NewValue;
var controlWithInputFocus = d as Control;
if (controlWithInputFocus != null)
{
if (isAttracted)
{
new KeyboardInputFocusEventManager(controlWithInputFocus);
}
}
}
public static void SetIsAttracted(DependencyObject dp, bool value)
{
dp.SetValue(IsAttracted, value);
}
public static bool GetIsAttracted(DependencyObject dp)
{
return (bool) dp.GetValue(IsAttracted);
}
private class KeyboardInputFocusEventManager
{
private readonly Control _control;
private Window _window;
public KeyboardInputFocusEventManager(Control control)
{
_control = control;
_control.Loaded += ControlLoaded;
_control.IsVisibleChanged += ControlIsVisibleChanged;
_control.Unloaded += ControlUnloaded;
}
private void ControlLoaded(object sender, RoutedEventArgs e)
{
_window = Window.GetWindow(_control);
if (_window != null)
{
_control.Unloaded += ControlUnloaded;
_control.IsVisibleChanged += ControlIsVisibleChanged;
if (_control.IsVisible)
{
_window.PreviewKeyDown += ParentWindowPreviewKeyDown;
}
}
}
private void ControlUnloaded(object sender, RoutedEventArgs e)
{
_control.Unloaded -= ControlUnloaded;
_control.IsVisibleChanged -= ControlIsVisibleChanged;
}
private void ControlIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (_window != null)
{
_window.PreviewKeyDown -= ParentWindowPreviewKeyDown;
}
if (_control.IsVisible)
{
_window = Window.GetWindow(_control);
if (_window != null)
{
_window.PreviewKeyDown += ParentWindowPreviewKeyDown;
}
}
}
private void ParentWindowPreviewKeyDown(object sender, KeyEventArgs e)
{
Keyboard.Focus(_control);
}
}
}