How to capture instantiation of DataTemplate in WPF? - c#

I have a search window, which looks like following:
The part with Condition and Options is a ContentControl with several DataTemplates, which contain different filter form for specific field (eg. datetime picker etc.).
I'd like specific control in the DataTemplate to be focused after opening the window (this is the X problem if someone asked)
I'm doing that in the following way:
public FindWindow(FindModel model)
{
InitializeComponent();
this.viewModel = Dependencies.Container.Instance.Resolve<FindWindowViewModel>(new ParameterOverride("access", this), new ParameterOverride("model", model));
DataContext = viewModel;
FocusInput();
}
FocusInput does the following:
public static FrameworkElement GetControlByName(DependencyObject parent, string name)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < count; ++i)
{
var child = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (child != null)
{
if (child.Name == name)
{
return child;
}
var descendantFromName = GetControlByName(child, name);
if (descendantFromName != null)
{
return descendantFromName;
}
}
}
return null;
}
public void FocusInput()
{
Dispatcher.Invoke(DispatcherPriority.ContextIdle, new Action(() =>
{
var obj = GetControlByName(filterContainer, "input");
if (obj != null && obj is Control ctl)
ctl.Focus();
}));
}
When it runs in the ctor, FindWindow gets null obj (despite ContentControl having Content set). However, when you click "Test" button, which simply runs FocusControl, the latter in turn finds required control and focuses it.
The question is: how to capture moment, when ContentControl finishes instantiating DataTemplate, such that I can capture required control? (Problem Y)
I'll be grateful for solution to either problem X or Y (which is my attempted solution).

Try to call FocusInput() once the window or ContentControl has been loaded:
public FindWindow(FindModel model)
{
InitializeComponent();
this.viewModel = Dependencies.Container.Instance.Resolve<FindWindowViewModel>(new ParameterOverride("access", this), new ParameterOverride("model", model));
DataContext = viewModel;
Loaded += (s, e) => FocusInput();
}

Maybe, a better solution would be behavior. It is inherited from
using System.Windows.Interactivity;
And is very similar to the previous answer, and is reusable
public class FocusBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
if(AssociatedObject!=null)
{
//LOADED EVENT SUBSCRIBE
AssociatedObject.Loaded += //YOUR FOCUS METHOD;
}
}
protected override void OnDetaching()
{
base.OnDetaching();
//LOADED EVENT UNSUBSCRIBE
AssociatedObject.Loaded -= //YOUR FOCUS METHOD;
}
}
and then attach it to your element in XAML:
<TextBox
x:Name="MainText">
<i:Interaction.Behaviors>
<behaviors:FocusBehavior />
</i:Interaction.Behaviors>
</TextBox>

Related

Validation.ErrorEvent is not rised when element with error is removed

I have to show a list where each item will be validated. I am subscribed to Validation.ErrorEvent on a top level to monitor for children.
When I remove item with validation error from list this event is not rised.
In example below I have 3 TextBox on screen, each is bound to int property. Entering wrong value will fire event (Title is changed to "+"), fixing value afterwards will fire event once (Title is changed to "-").
However removing TextBox while having error will not rise event (to clean up) and Title stay "+":
How can I fix that? Ideally I want that this event is automatically rised before removing happens.
Please note: in real project there is complex hierarchy of view models, solutions like "set Title in delete method" would require monitoring for sub-views and propagating that info through all hierarchy, which I'd like to avoid. I'd prefer view-only solution.
MCVE:
public partial class MainWindow : Window
{
public ObservableCollection<VM> Items { get; } = new ObservableCollection<VM> { new VM(), new VM(), new VM() };
public MainWindow()
{
InitializeComponent();
AddHandler(Validation.ErrorEvent, new RoutedEventHandler((s, e) =>
Title = ((ValidationErrorEventArgs)e).Action == ValidationErrorEventAction.Added ? "+" : "-"));
DataContext = this;
}
void Button_Click(object sender, RoutedEventArgs e) => Items.RemoveAt(0);
}
public class VM
{
public int Test { get; set; }
}
xaml:
<StackPanel>
<ItemsControl ItemsSource="{Binding Items}" Height="200">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Test, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Remove first" Click="Button_Click" />
</StackPanel>
After a little time, I have a working solution that you could start with. As already mentioned by Ed, You're using the UI as a data structure, which is never a good idea. The MVVM way to do validation is IDataErrorInfo, this is true and really you should be implementing the IDataErrorInfo interface to handle these errors.
On another note, here's what I did to get it working. I am handling the CollectionChanged event for the ObservableCollection of your VM's. When the collection changes, you need to find the element that was actually being removed, if found, we can try and clear it's ValidationError object for that element itself.
Here is the class -
public partial class MainWindow : Window
{
public ObservableCollection<VM> Items { get; } = new ObservableCollection<VM> { new VM(), new VM(), new VM() };
public MainWindow()
{
InitializeComponent();
Items.CollectionChanged += Items_CollectionChanged;
AddHandler(Validation.ErrorEvent, new RoutedEventHandler((s, e) =>
Title = ((ValidationErrorEventArgs)e).Action == ValidationErrorEventAction.Added ? "+" : "-"));
DataContext = this;
}
private void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove) {
foreach (TextBox tb in FindVisualChildren<TextBox>(this))
{
if(tb.DataContext == e.OldItems[0])
{
Validation.ClearInvalid(tb.GetBindingExpression(TextBox.TextProperty));
break;
}
}
}
}
private void Button_Click(object sender, RoutedEventArgs e) => Items.RemoveAt(0);
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
}
public class VM
{
public int Test { get; set; }
}
The bread and butter to make this work is Validation.ClearInvalid(tb.GetBindingExpression(TextBox.TextProperty)); which actually removes all ValidationError objects from the BindingExpressionBase object which in this case is TextBox.TextProperty.
Note: There has been no error checking here, you may want to do that.

Bind command in view model to keyboard shortcut

I'm using C#, WPF, ReactiveUI and Prism to create an application with many different views (user controls). On some views there are buttons/menu items that bind to a command in the view model. I would like these buttons to also activate using a key combination such as ctrl+s, etc....
What I've tried
InputBindings but that only works when the view that defines these input bindings has focus.
ApplicationCommands the predefined commands like ApplicationCommands.Close seem useful. I can reference them both in the view and the view model, but I don't know how subscribe to them in my view model. It also seems that I have to 'activate' the command first, or at least change CanExecute since any button bound to such command stays disabled.
What I wish for
Let's say I have a view that represents the top menu bar MenuView with a button myButton and a corresponding view model MenuViewModel with a command myCommand. I would like to bind myButton to myCommand and the keyboard shortcut ctrl+u to myCommand without MenuView knowing about the implementation of its view model. The keyboard shortcut should work as long as the window that contains MenuView has focus.
I don't really care if the keyboard short-cut is either in the view or view model.
You could create an attached Blend behaviour that handles the PreviewKeyDown event of the parent window:
public class KeyboardShortcutBehavior : Behavior<FrameworkElement>
{
private Window _parentWindow;
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(nameof(Command), typeof(ICommand),
typeof(KeyboardShortcutBehavior), new FrameworkPropertyMetadata(null));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty ModifierKeyProperty =
DependencyProperty.Register(nameof(ModifierKey), typeof(ModifierKeys),
typeof(KeyboardShortcutBehavior), new FrameworkPropertyMetadata(ModifierKeys.None));
public ModifierKeys ModifierKey
{
get { return (ModifierKeys)GetValue(ModifierKeyProperty); }
set { SetValue(ModifierKeyProperty, value); }
}
public static readonly DependencyProperty KeyProperty =
DependencyProperty.Register(nameof(Key), typeof(Key),
typeof(KeyboardShortcutBehavior), new FrameworkPropertyMetadata(Key.None));
public Key Key
{
get { return (Key)GetValue(KeyProperty); }
set { SetValue(KeyProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObject_Loaded;
AssociatedObject.Unloaded += AssociatedObject_Unloaded;
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
_parentWindow = Window.GetWindow(AssociatedObject);
if(_parentWindow != null)
{
_parentWindow.PreviewKeyDown += ParentWindow_PreviewKeyDown;
}
}
private void ParentWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
if(Command != null && ModifierKey != ModifierKeys.None && Key != Key.None && Keyboard.Modifiers == ModifierKey && e.Key == Key)
Command.Execute(null);
}
private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
{
if(_parentWindow != null)
{
_parentWindow.PreviewKeyDown -= ParentWindow_PreviewKeyDown;
}
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= AssociatedObject_Loaded;
AssociatedObject.Unloaded -= AssociatedObject_Loaded;
}
}
Sample usage:
<TextBox xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:Interaction.Behaviors>
<local:KeyboardShortcutBehavior ModifierKey="Ctrl" Key="U" Command="{Binding myCommand}" />
</i:Interaction.Behaviors>
</TextBox>
In code behind easy. Create some utility function that eventually lead to an observable of the parent window key events. Note that you will need the ReactiveUI.Events library.
Some utils for handling load and unload of controls.
public static void LoadUnloadHandler
( this FrameworkElement control
, Func<IDisposable> action
)
{
var state = false;
var cleanup = new SerialDisposable();
Observable.Merge
(Observable.Return(control.IsLoaded)
, control.Events().Loaded.Select(x => true)
, control.Events().Unloaded.Select(x => false)
)
.Subscribe(isLoadEvent =>
{
if (!state)
{
// unloaded state
if (isLoadEvent)
{
state = true;
cleanup.Disposable = new CompositeDisposable(action());
}
}
else
{
// loaded state
if (!isLoadEvent)
{
state = false;
cleanup.Disposable = Disposable.Empty;
}
}
});
}
public static IObservable<T> LoadUnloadHandler<T>(this FrameworkElement control, Func<IObservable<T>> generator)
{
Subject<T> subject = new Subject<T>();
control.LoadUnloadHandler(() => generator().Subscribe(v => subject.OnNext(v)));
return subject;
}
and one specifically for handling the window of a loaded control
public static IObservable<T> LoadUnloadHandler<T>
(this FrameworkElement control, Func<Window, IObservable<T>> generator)
{
Subject<T> subject = new Subject<T>();
control.LoadUnloadHandler(() => generator(Window.GetWindow(control)).Subscribe(v => subject.OnNext(v)));
return subject;
}
and finally a key handler for the parent window of any control
public static IObservable<KeyEventArgs> ParentWindowKeyEventObservable(this FrameworkElement control)
=> control.LoadUnloadHandler((Window window) => window.Events().PreviewKeyDown);
now you can do
Button b;
b.ParentWindowKeyEventObservable()
.Subscribe( kEvent => {
myCommand.Execute();
}
It might seem a bit complex but I use the LoadUnloadHandler on most user controls to aquire and dispose resources as the UI lifecycle progresses.
You want to use KeyBindings for this. This allows you to bind keyboard key combos to a command. Read the docs here: https://msdn.microsoft.com/en-us/library/system.windows.input.keybinding(v=vs.110).aspx

Call RelayCommand in another class MVVM

I'm using a RelayCommand with delegate access in my project. it's a call with a behavior. When I used Directly in ViewModel it works fine.
but I would like to pass this command in a class in order to use it generically. the behavior still works but no sign of life of the command.
The command in my ViewModel(Work):
LoadCommand = new RelayCommand<object>( new Action<object>(
obj =>
{
if (Busy)
return;
Busy = true;
System.Threading.ThreadPool.QueueUserWorkItem(
delegate
{
Application.Current.Dispatcher.BeginInvoke(new Action(
delegate
{
// myReference.AddDatas(Mvts);
AddMoreItems();
Busy = false;
}));
});
}));
And the command in my new class (doesn't work):
public Action<object> LoadCommand(Ref<ObservableCollection<T>> myList)
{
return new Action<object>(
obj =>
{
if (Busy)
return;
Busy = true;
System.Threading.ThreadPool.QueueUserWorkItem(
delegate
{
Application.Current.Dispatcher.Invoke(new Action(
delegate
{
AddDatas(myList);
Busy = false;
}));
});
}));
Call with
LoadCommand = new RelayCommand<object>(myReference.LoadCommand(Mvts));
All I know is that after putting breakpoints at the beginning of the order, it is not called.
For more informations my behavior call LoadCommand when scroll of datagrid is bottom
public class ScrollViewerMonitor
{
public static DependencyProperty AtEndCommandProperty
= DependencyProperty.RegisterAttached(
"AtEndCommand", typeof(ICommand),
typeof(ScrollViewerMonitor),
new PropertyMetadata(OnAtEndCommandChanged));
public static ICommand GetAtEndCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(AtEndCommandProperty);
}
public static void SetAtEndCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(AtEndCommandProperty, value);
}
public static void OnAtEndCommandChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
if (element != null)
{
element.Loaded -= element_Loaded;
element.Loaded += element_Loaded;
}
}
static void element_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
element.Loaded -= element_Loaded;
ScrollViewer scrollViewer = FindChildOfType<ScrollViewer>(element);
if (scrollViewer == null)
{
return;
}
var dpd = DependencyPropertyDescriptor.FromProperty(ScrollViewer.VerticalOffsetProperty, typeof(ScrollViewer));
dpd.AddValueChanged(scrollViewer, delegate (object o, EventArgs args)
{
bool atBottom = scrollViewer.VerticalOffset
>= scrollViewer.ScrollableHeight;
if (atBottom)
{
var atEnd = GetAtEndCommand(element);
if (atEnd != null)
{
atEnd.Execute(null);
}
}
});
}
static T FindChildOfType<T>(DependencyObject root) where T : class
{
var queue = new Queue<DependencyObject>();
queue.Enqueue(root);
while (queue.Count > 0)
{
DependencyObject current = queue.Dequeue();
for (int i = VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--)
{
var child = VisualTreeHelper.GetChild(current, i);
var typedChild = child as T;
if (typedChild != null)
{
return typedChild;
}
queue.Enqueue(child);
}
}
return null;
}
}
although it reached in both cases atEnd.Execute(null);
But when command execute in other file the property below IsAlive = False
and when command is directly execute in VM IsAlive is to True with my target object
There is no chance to call Command, cause you are calling not the same instance of your viewModel.
To make some actions(for example, run your Command or edit some property) in your viewModel from another viewModel, it is better to use EventAggregator pattern.
In my view, the best approach is using EventAggregator pattern of Prism framework. The Prism simplifies MVVM pattern. However, if you have not used Prism, you can use Rachel Lim's tutorial - simplified version of EventAggregator pattern by Rachel Lim. I highly recommend you Rachel Lim's approach.
If you use Rachel Lim's tutorial, then you should create a common class:
public static class EventSystem
{...Here Publish and Subscribe methods to event...}
And publish an event into your OptionViewModel:
eventAggregator.GetEvent<ChangeStockEvent>().Publish(
new TickerSymbolSelectedMessage{ StockSymbol = “STOCK0” });
then you subscribe in constructor of another your MainViewModel to an event:
eventAggregator.GetEvent<ChangeStockEvent>().Subscribe(ShowNews);
public void ShowNews(TickerSymbolSelectedMessage msg)
{
// Handle Event
}
The Rachel Lim's simplified approach is the best approach that I've ever seen. However, if you want to create a big application, then you should read this article by Magnus Montin and at CSharpcorner with an example.
Those two are completely different, the first one is some kind of property which shows a proper Command binding and second one is a method returning RelayCommand. I dont think you can bind a Command property to a method. That might be reason why it doesnt work.

Creating Items DP for charting user control

I am busy creating a user control that has some basic charting/graph functions. In essence I want to have an "Items" dependency property to which the user of the control can bind. The control will then display all the items and updates made to the source.
What I have done so far was to create an "Items" DP in my user control, code behind.
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items",
typeof(ObservableCollection<Polyline>),
typeof(RPGraph),
new FrameworkPropertyMetadata(
new ObservableCollection<Polyline>(),
new PropertyChangedCallback(OnItemsChanged)));
public ObservableCollection<Polyline> Items
{
get { return (ObservableCollection<Polyline>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static void OnItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
My first stumbling block was that "OnItemsChanged" didn't get called when my collection changed. After a couple of hours I found a stackoverflow post explaining why (ObservableCollection dependency property does not update when item in collection is deleted). Following this advice solved one part of my problem. Now I could Add new items (Polylines) to the ObservableCollection list. But what if I added an extra point or modified a point in the Polyline. Armed with the knowledge of the previous problem I found the Points.Changed event. I then subscribed to it and placed the update code in there.
This finally works, but man there must be a better or more elegant way of achieving this (as stated at the top), which I think all boils down to not using ObservableCollection? Any advice?
Below is the working OnItemChanged method (excuse the draft code, I just wanted to get it working :-) :
public static void OnItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = d as RPGraph;
foreach (Polyline poly in thisControl.Items)
thisControl.manager.Items.Add(poly.Points.ToArray());
if (e.OldValue != null)
{
var coll = (INotifyCollectionChanged)e.OldValue;
// Unsubscribe from CollectionChanged on the old collection
coll.CollectionChanged -= Items_CollectionChanged;
}
if (e.NewValue != null)
{
var coll = (ObservableCollection<Polyline>)e.NewValue;
// Subscribe to CollectionChanged on the new collection
coll.CollectionChanged += (o, t) => {
ObservableCollection<Polyline> items = o as ObservableCollection<Polyline>;
thisControl.manager.Items.Add(items[t.NewStartingIndex].Points.ToArray());
foreach (Polyline poly in items)
{
poly.Points.Changed += (n, m) => {
for (int i = 0; i < thisControl.manager.Items.Count; i++)
thisControl.manager.Items[i] = thisControl.Items[i].Points.ToArray();
thisControl.manager.DrawGraph(thisControl.graphView);
};
}
thisControl.manager.DrawGraph(thisControl.graphView);
};
}
thisControl.manager.DrawGraph(thisControl.graphView);
}
You are completely right, an ObservableCollection does not notify when any of its items changes its property value.
You could extend the functionality of ObservableCollection adding notifications for these cases.
It may look like this:
public sealed class ObservableNotifiableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
public event ItemPropertyChangedEventHandler ItemPropertyChanged;
public event EventHandler CollectionCleared;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
base.OnCollectionChanged(args);
if (args.NewItems != null)
{
foreach (INotifyPropertyChanged item in args.NewItems)
{
item.PropertyChanged += this.OnItemPropertyChanged;
}
}
if (args.OldItems != null)
{
foreach (INotifyPropertyChanged item in args.OldItems)
{
item.PropertyChanged -= this.OnItemPropertyChanged;
}
}
}
protected override void ClearItems()
{
foreach (INotifyPropertyChanged item in this.Items)
{
item.PropertyChanged -= this.OnItemPropertyChanged;
}
base.ClearItems();
this.OnCollectionCleared();
}
private void OnCollectionCleared()
{
EventHandler eventHandler = this.CollectionCleared;
if (eventHandler != null)
{
eventHandler(this, EventArgs.Empty);
}
}
private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs args)
{
ItemPropertyChangedEventHandler eventHandler = this.ItemPropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new ItemPropertyChangedEventArgs(sender, args.PropertyName));
}
}
}
Then you can subscribe to the ItemPropertyChanged event and do your stuff.

autocompletebox focus in wpf

when I try to focus on my "autocompletetextbox" I failed I write autocompletetextbox.focus()
but the cursor still focus in another what should I do or write to enable to write in it or focus?
I experienced the same thing -- it does not work properly in its current form (I expect you're talking about the AutoCompleteBox that comes with the February 2010 release of WPFToolkit).
I created a subclass:
public class AutoCompleteFocusableBox : AutoCompleteBox
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var textbox = Template.FindName("Text", this) as TextBox;
if(textbox != null) textbox.Focus();
}
}
This sets focus to the actual TextBox (called "Text") that is part of the default ControlTemplate.
You will have to override the Focus method to find the template of the Textbox.
public class FocusableAutoCompleteBox : AutoCompleteBox
{
public new void Focus()
{
var textbox = Template.FindName("Text", this) as TextBox;
if (textbox != null) textbox.Focus();
}
}
This is very old question, but I want to share my work-around.
Keyboard.Focus(autocompletetextbox);
autocompletetextbox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
This works in WPFToolkit v3.5.50211.1 on Visual Studio Express 2015 for Windows Desktop
It seems that you have to wait for the auto complete box to load first. Then set focus
<sdk:AutoCompleteBox
x:Name="_employeesAutoCompleteBox"
ItemsSource="{Binding Path=Employees}"
SelectedItem="{Binding SelectedEmployee, Mode=TwoWay}"
ValueMemberPath="DisplayName" >
</sdk:AutoCompleteBox>
_employeesAutoCompleteBox.Loaded +=
(sender, e) => ((AutoCompleteBox)sender).Focus();
This is my solution,
I found it easier than having inherited class
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
var textBox = FindVisualChild<TextBox>(CodedCommentBox);
textBox.Focus();
}
private TChildItem FindVisualChild<TChildItem>(DependencyObject obj) where TChildItem : DependencyObject
{
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
var item = child as TChildItem;
if (item != null)
{
return item;
}
var childOfChild = FindVisualChild<TChildItem>(child);
if (childOfChild != null)
{
return childOfChild;
}
}
return null;
}
This is my solution for setting focus on AutoCompleteTextBox control Text:
private void MyPageLoaded(object sender, RoutedEventArgs e)
{
var myPage = (MyControl)sender;
var autoTextBox = (AutoCompleteTextBox)myPage.FindName("AutoTextBox");
if (autoTextBox != null)
{
var innerTextBox = autoTextBox.textBox;
if (innerTextBox != null)
{
innerTextBox.Focus();
}
}
}
You could also use a extension method for this:
public static void ForceFocus(this AutoCompleteBox box)
{
if (box.Template.FindName("Text", box) is TextBox textbox)
{
textbox.Focus();
}
}

Categories