If I globally enable spell checking in App.xaml...
<Application.Resources>
<Style TargetType="TextBox">
<Setter Property="SpellCheck.IsEnabled"
Value="True" />
</Style>
</Application.Resources>
...then I get red underlines and spell checking in all textboxes in the application, irrespective of where they are.
If I want to add a custom dictionary, then I have to use code similar to the one shown in this SO answer, and then call it as follows...
public MainWindow() {
InitializeComponent();
Loaded += (_, __) => Helpers.SetCustomDictionary(this);
}
(code for helper method shown lower down)
This works fine for textboxes that are shown when the window first loads, but if I have a tab control, and the default tab has a textbox, then the custom dictionary is not applied.
I tried calling Helpers.SetCustomDictionary(this) when the tab loaded, but that didn't work either. I can see that the method is called when the window loads, and my guess is that at that stage, the tab's contents haven't been created, so the method doesn't find them to set the custom dictionary.
The only thing I found that worked was calling it when the individual textbox itself was loaded. However, this is painful, as I have to do this for every single textbox individually.
Anyone know of a way to get the custom dictionary working for textboxes that are not visible when the window first loads?
Thanks
P.S. Here is the code for the helper method, which uses the FindAllChildren() method shown in the linked SO reply...
public static void SetCustomDictionary(DependencyObject parent) {
Uri uri = new Uri("pack://application:,,,/CustomDictionary.lex");
List<TextBox> textBoxes = new List<TextBox>();
FindAllChildren(parent, ref textBoxes);
foreach (TextBox tb in textBoxes) {
if (tb.SpellCheck.IsEnabled && !tb.SpellCheck.CustomDictionaries.Contains(uri)) {
tb.SpellCheck.CustomDictionaries.Add(uri);
}
}
}
There is probably a better solution but you could use OnStartup event of your App.xaml.cs to set the dictionary for every TextBox when it loads with a single event handler:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
EventManager.RegisterClassHandler(typeof(TextBox), FrameworkElement.LoadedEvent, new RoutedEventHandler(TextBox_OnLoaded));
}
private void TextBox_OnLoaded(object sender, RoutedEventArgs e)
{
// Set custom dictionary here.
}
}
Whilst Dean Kuga's answer looked promising, it didn't work for me. After some more searching, it seems that there is a bug that prevents the Loaded event from being fired in most cases. In a comment to the answer to the SO question where I saw this mentioned, Marcin Wisnicki linked to some code he wrote that works around the issue.
As I only want this to work for textboxes, I simplified his code a little. In case it helps anyone, here is my simplified code...
public partial class App {
protected override void OnStartup(StartupEventArgs e) {
base.OnStartup(e);
EventManager.RegisterClassHandler(typeof(Window),
FrameworkElement.SizeChangedEvent, new RoutedEventHandler(OnSizeChanged));
EventManager.RegisterClassHandler(typeof(TextBox),
FrameworkElement.LoadedEvent, new RoutedEventHandler(OnLoaded), true);
}
private static void OnSizeChanged(object sender, RoutedEventArgs e) {
SetMyInitialised((Window)sender, true);
}
private static void OnLoaded(object sender, RoutedEventArgs e) {
if (e.OriginalSource is TextBox) {
TextBox tb = (TextBox)e.OriginalSource;
Helpers.SetCustomDictionaryTextBox(tb);
}
}
#region MyInitialised dependency property
public static readonly DependencyProperty MyInitialisedProperty =
DependencyProperty.RegisterAttached("MyInitialised",
typeof(bool),
typeof(App),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.Inherits,
OnMyInitialisedChanged));
private static void OnMyInitialisedChanged(DependencyObject dpo,
DependencyPropertyChangedEventArgs ev) {
if ((bool)ev.NewValue && dpo is FrameworkElement) {
(dpo as FrameworkElement).Loaded += delegate { };
}
}
public static void SetMyInitialised(UIElement element, bool value) {
element.SetValue(MyInitialisedProperty, value);
}
public static bool GetMyInitialised(UIElement element) {
return (bool)element.GetValue(MyInitialisedProperty);
}
#endregion MyInitialised
}
Being English, I changed "Initialized" to "Initialised", but other than that, the DP code is the same.
Hope this helps someone.
Related
I've been going back and forth over the pros and cons of the two following approaches to Events from custom controls. My debate basically revolves around how much "logic" should be placed within a custom (not user) control and to best get events into a viewmodel.
The "control", DataGridAnnotationControl, resides within an adorner to my data grid. The goal here is to respond to the user selecting an item from a combobox displayed within the custom control.
The first example, Example #1, uses a pretty standard custom event in the DataGridAnnotationControl
which is then mapped by way of the adorner to the target AppointmentEditor (viewmodel). My biggest complaint with this is the obvious dependency to the (AppointmentEditor) from the adorner to achieve proper event routing.
♦ Example #1:
♦ CustomControl DataGridAnnotationControl
public override void OnApplyTemplate()
{
......
_cboLastName.SelectionChanged += _cboLastName_SelectionChanged;
}
private void _cboLastName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RaiseSelectionChanged();
}
public event Action SelectionChanged;
public void RaiseSelectionChanged()
{
SelectionChanged?.Invoke();
}
♦ Adorner DataGridAnnotationAdorner
public DataGridAnnotationAdorner(DataGrid adornedDataGrid)
: base(adornedDataGrid)
{
......
Control = new DataGridAnnotationControl();
this.SelectionChanged += ((AppointmentEditor)adornedDataGrid.DataContext).SelectionChanged; <--This requires a reference to Patient_Registration.Editors. THIS IS FORCING
A DEPENDENCY ON THE PATIENT_REGISTRATION PROJECT.
}
public event Action SelectionChanged
{
add { Control.SelectionChanged += value; }
remove { Control.SelectionChanged -= value; }
}
♦ AppointmentEditor
public void SelectionChanged()
{
throw new NotImplementedException();
}
Example #2 This example uses pretty standard event routing up to the mainwindow from which an event aggregator is being used to hit the AppointmentEditor as a subscriber to the event. My biggest complaint here is all the additional code needed (over Example #1). In addition, it seems like a complicating factor to climb the visual tree just to jump into the one viewmodel designed to support this customcontrol.
Example #2:
♦ CustomControl DataGridAnnotationControl
public override void OnApplyTemplate()
{
.....
_cboLastName.SelectionChanged += _cboLastName_SelectionChanged;
}
private void _cboLastName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RaisePatientNameSelectionChangedEvent();
}
public static readonly RoutedEvent PatientNameSelectionChangedEvent = EventManager.RegisterRoutedEvent(
"PatientNameSelectionChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DataGridAnnotationControl));
// Provide CLR accessors for the event
public event RoutedEventHandler PatientNameSelectionChanged
{
add { AddHandler(PatientNameSelectionChangedEvent, value); }
remove { RemoveHandler(PatientNameSelectionChangedEvent, value); }
}
protected virtual void RaisePatientNameSelectionChangedEvent()
{
RoutedEventArgs args = new RoutedEventArgs(DataGridAnnotationControl.PatientNameSelectionChangedEvent);
RaiseEvent(args);
}
♦ public partial class MainWindow : Window
{
public MainWindow(IMainWindowViewModel mainWindowViewModel, EventAggregator eventAggregator)
{
InitializeComponent();
EventAggregator = eventAggregator;
DataContext = mainWindowViewModel;
....
AddHandler(DataGridAnnotationControl.PatientNameSelectionChangedEvent, new RoutedEventHandler(PatientNameSelectionChangedHandler));
}
private void PatientNameSelectionChangedHandler(object sender, RoutedEventArgs e)
{
EventAggregator.PublishEvent( new PatientNameSelected() );
}
}
♦ public class AppointmentEditor : INotifyPropertyChanged, ISubscriber<PatientNameSelected>
public void OnEventHandlerAsync(PatientNameSelected e)
{
throw new NotImplementedException();
}
Is there a preferred way of doing this?
TIA
Ideally, your custom control should have no knowledge of your view-models.
Using MVVM, you would bind an event in your custom control to a command in your view-model.
I author and maintain tons of custom controls that are used by a lot of other teams. I always expose an associated ICommand with any event to make it easy for MVVM users to use my controls in the easiest way possible.
I'm using the MahApps Metro window style, and I want to capture the event when the user clicks on the close button of the window.
I've set my ShutdownMode to OnExplicitShutdown so I need to call Application.Current.Shutdown(); when that button is clicked
How can I do this ?
I believe that I am also trying to do the same thing as you (bind to the close window button) using WPF and MahApps.Metro. I wasn't able to find a way yet to bind to that command explicitly, but I was able to accomplish this by setting the ShowCloseButton property to false (to hide it) and then created my own close window command button and handled the logic in my viewmodel. Took me some digging, but I found that you can easily add your own window command controls in the command bar with MahApps.Metro just add similar markup to your XAML:
<Controls:MetroWindow.WindowCommands>
<Controls:WindowCommands>
<Button Content="X" Command="{Binding CancelCommand}" />
</Controls:WindowCommands>
</Controls:MetroWindow.WindowCommands>
You need to create a DependencyProperty to handle the close window behavior:
DependencyProperty
namespace MyApp.DependencyProperties
{
public class WindowProperties
{
public static readonly DependencyProperty WindowClosingProperty =
DependencyProperty.RegisterAttached("WindowClosing", typeof(RelayCommand), typeof(WindowProperties), new UIPropertyMetadata(null, WindowClosing));
public static object GetWindowClosing(DependencyObject depObj)
{
return (RelayCommand)depObj.GetValue(WindowClosingProperty);
}
public static void SetWindowClosing(DependencyObject depObj, RelayCommand value)
{
depObj.SetValue(WindowClosingProperty, value);
}
private static void WindowClosing(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var element = (Window)depObj;
if (element != null)
element.Closing += OnWindowClosing;
}
private static void OnWindowClosing(object sender, CancelEventArgs e)
{
RelayCommand command = (RelayCommand)GetWindowClosing((DependencyObject)sender);
command.Execute((Window)sender);
}
}
}
In your ViewModel
public RelayCommand WindowClosedCommand { get; set; }
private void WindowClose()
{
Application.Current.Shutdown();
}
In Constructor of ViewModel
this.WindowCloseCommand = new RelayCommand(WindowClose);
In your XAML
<mah:MetroWindow x:Class="MyApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:dp="clr-namespace:MyApp.DependencyProperties"
dp:WindowProperties.WindowClosing="{Binding WindowClosedCommand}" />
Since the solution from gotapps.net did't worked for me because I did't found how to programatically do that (My window does not have the Xaml file, it's just a base class). I found another workaround to use the same button to close the Window as follows:
internal class BaseWindow : MetroWindow
{
public BaseWindow()
{
this.Loaded += BaseWindow_Loaded;
}
void BaseWindow_Loaded(object sender, EventArgs e)
{
Button close = this.FindChild<Button>("PART_Close");
close.Click += close_Click;
}
void close_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown(0);
}
}
You can use "Closing" or "Closed" event
Closing handler gets triggered when user clicks on the Close button. This also gives you control on whether the application should be closed.
Similarly, Closed handler gets triggered just before the window is closed
<Controls:MetroWindow x:Class="MyClass.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="My Class"
Closing="MainWindow_OnClosing">
</Controls:MetroWindow>
I have a textbox on my wpfgrid that I need to perform some tekst inserting and such on. To do this, the textbox is referenced into the presentationmodel from the view, ReferenceToTextBox (we do MVP with Prism). Also, the textbox in the view TextDescription is bound to the Description-property on the model.
We also have a dropdown-list containing some predefined text-blobs (adresses, VAT-numbers and such). When you choose one of these, they should be inserted into the textbox at the carets current position. Since you can't bind on CaretIndex, the above mentioned workaround is made. The dropdown-list is bound on SelectedItem to a property on the model, so when the SelectedItem changes, the property changes, and in the setter on the property a method is called to insert the text of the selected-item into the ReferenceToTextBox "virtual" textbox in the model (which should be just a reference to the textbox in the view).
However, if I delete all the text from the textbox in the view and add a new predefined text-blob. The ReferenceToTextBox.Text property still contains all the text that I deleted. It seems like the ReferenceToTextBox is no longer just a reference, but a whole own textbox. Which makes it even weirder when updates to ReferenceToTextBox.Text actually updates the "visual" textbox on the view.
What is actually happening here?
Not a direct answer to your question, but what about a derived TextBox class that actually allows binding to its CaretIndex property:
public class TextBoxEx : TextBox
{
public static readonly DependencyProperty CaretIndexProperty = DependencyProperty.Register(
"CaretIndex", typeof(int), typeof(TextBoxEx),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CaretIndexChanged));
public new int CaretIndex
{
get { return (int)GetValue(CaretIndexProperty); }
set { SetValue(CaretIndexProperty, value); }
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
CaretIndex = base.CaretIndex;
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
CaretIndex = base.CaretIndex;
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
CaretIndex = base.CaretIndex;
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
CaretIndex = base.CaretIndex;
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
CaretIndex = base.CaretIndex;
}
private static void CaretIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (obj is TextBox)
{
((TextBox)obj).CaretIndex = (int)e.NewValue;
}
}
}
I have a Silverlight 2 application that validates data OnTabSelectionChanged. Immediately I began wishing that UpdateSourceTrigger allowed more than just LostFocus because if you click the tab without tabbing off of a control the LINQ object is not updated before validation.
I worked around the issue for TextBoxes by setting focus to another control and then back OnTextChanged:
Private Sub OnTextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
txtSetFocus.Focus()
sender.Focus()
End Sub
Now I am trying to accomplish the same sort of hack within a DataGrid. My DataGrid uses DataTemplates generated at runtime for the CellTemplate and CellEditingTemplate. I tried writing the TextChanged="OnTextChanged" into the TextBox in the DataTemplate, but it is not triggered.
Anyone have any ideas?
You can do it with a behavior applied to the textbox too
// xmlns:int is System.Windows.Interactivity from System.Windows.Interactivity.DLL)
// xmlns:behavior is your namespace for the class below
<TextBox Text="{Binding Description,Mode=TwoWay,UpdateSourceTrigger=Explicit}">
<int:Interaction.Behaviors>
<behavior:TextBoxUpdatesTextBindingOnPropertyChanged />
</int:Interaction.Behaviors>
</TextBox>
public class TextBoxUpdatesTextBindingOnPropertyChanged : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.TextChanged += new TextChangedEventHandler(TextBox_TextChanged);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.TextChanged -= TextBox_TextChanged;
}
void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var bindingExpression = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();
}
}
This blog post shows how to update the source of a textbox explicitly using attached property:
http://www.thomasclaudiushuber.com/blog/2009/07/17/here-it-is-the-updatesourcetrigger-for-propertychanged-in-silverlight/
You could easily modify it to work with other controls as well...
I ran into this same problem using MVVM and Silverlight 4. The problem is that the binding does not update the source until after the textbox looses focus, but setting focus on another control doesn't do the trick.
I found a solution using a combination of two different blog posts. I used the code from Patrick Cauldwell's DefaultButtonHub concept, with one "SmallWorkaround" from SmallWorkarounds.net
http://www.cauldwell.net/patrick/blog/DefaultButtonSemanticsInSilverlightRevisited.aspx
www.smallworkarounds.net/2010/02/elementbindingbinding-modes.html
My change resulted in the following code for the DefaultButtonHub class:
public class DefaultButtonHub
{
ButtonAutomationPeer peer = null;
private void Attach(DependencyObject source)
{
if (source is Button)
{
peer = new ButtonAutomationPeer(source as Button);
}
else if (source is TextBox)
{
TextBox tb = source as TextBox;
tb.KeyUp += OnKeyUp;
}
else if (source is PasswordBox)
{
PasswordBox pb = source as PasswordBox;
pb.KeyUp += OnKeyUp;
}
}
private void OnKeyUp(object sender, KeyEventArgs arg)
{
if (arg.Key == Key.Enter)
if (peer != null)
{
if (sender is TextBox)
{
TextBox t = (TextBox)sender;
BindingExpression expression = t.GetBindingExpression(TextBox.TextProperty);
expression.UpdateSource();
}
((IInvokeProvider)peer).Invoke();
}
}
public static DefaultButtonHub GetDefaultHub(DependencyObject obj)
{
return (DefaultButtonHub)obj.GetValue(DefaultHubProperty);
}
public static void SetDefaultHub(DependencyObject obj, DefaultButtonHub value)
{
obj.SetValue(DefaultHubProperty, value);
}
// Using a DependencyProperty as the backing store for DefaultHub. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DefaultHubProperty =
DependencyProperty.RegisterAttached("DefaultHub", typeof(DefaultButtonHub), typeof(DefaultButtonHub), new PropertyMetadata(OnHubAttach));
private static void OnHubAttach(DependencyObject source, DependencyPropertyChangedEventArgs prop)
{
DefaultButtonHub hub = prop.NewValue as DefaultButtonHub;
hub.Attach(source);
}
}
This should be included in some sort of documentation for Silverlight :)
I know it's old news... but I got around this by doing this:
Text="{Binding Path=newQuantity, UpdateSourceTrigger=PropertyChanged}"
I'm new to WPF.
I have like 15 grids on my Window and I have a small menu on which I can click and choose which grid to show up or hide. One grid at a time only. I would like that grid to hode (fade out) when I hit Esc. I have all the animations already, I just need to know what grid is visible (active) at the moment.
I don't know how to get current topmost control of my Window.
My solution is when KeyDown event is triggered on my Window to:
private void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Escape)
{
//check all grids for IsVisible and on the one that is true make
BeginStoryboard((Storyboard)this.FindResource("theVisibleOne_Hide"));
}
}
By active, I assume that means the one that has keyboard focus. If so, the following will return the control that currently has keyboard input focus:
System.Windows.Input.Keyboard.FocusedElement
You could use it like this:
if (e.Key == System.Windows.Input.Key.Escape)
{
//check all grids for IsVisible and on the one that is true make
var selected = Keyboard.FocusedElement as Grid;
if (selected == null) return;
selected.BeginStoryboard((Storyboard)this.FindResource("HideGrid"));
}
An approach that would be more decoupled would be to create a static attached dependency property. It could be used like this (untested):
<Grid local:Extensions.HideOnEscape="True" .... />
A very rough implementation would look like:
public class Extensions
{
public static readonly DependencyProperty HideOnEscapeProperty =
DependencyProperty.RegisterAttached(
"HideOnEscape",
typeof(bool),
typeof(Extensions),
new UIPropertyMetadata(false, HideOnExtensions_Set));
public static void SetHideOnEscape(DependencyObject obj, bool value)
{
obj.SetValue(HideOnEscapeProperty, value);
}
public static bool GetHideOnEscape(DependencyObject obj)
{
return (bool)obj.GetValue(HideOnEscapeProperty);
}
private static void HideOnExtensions_Set(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = d as Grid;
if (grid != null)
{
grid.KeyUp += Grid_KeyUp;
}
}
private static void Grid_KeyUp(object sender, KeyEventArgs e)
{
// Check for escape key...
var grid = sender as Grid;
// Build animation in code, or assume a resource exists (grid.FindResource())
// Apply animation to grid
}
}
This would remove the need to have code in codebehind.