WPF changing several properties one after another - c#

I have a TabControl that contains a TextBox per tab. I need to select a tab programmatically and then set the selection of the TextBox and set the focus to the TextBox.
My problem is, when I first select a new tab in code and then set the focus to the textbox it doesn't work.
When the desired tab is already the selected tab and I set the focus to the textbox inside the tab, it's working.
It seems, that performing several actions on WPF elements does not work.
What is the right way to first switch the tab of a tabcontrol and then set the focus to a child in the newly selected TabItem?
Edit: I've found something on the Internet: Waiting for the render thread to complete:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/693fbedb-efa6-413e-ab66-530c6961d3fb/how-to-wait-for-the-wpf-render-thread-to-catch-up?forum=wpf
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => { })).Wait();
But is this the right way?
BTW: What is the difference between:
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => { })).Wait();
Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new Action(() => { }));

Use the FocusManager.SetFocusedElement() method.
Focus has some nuances, you can learn more here.
XAML
<TabControl>
<TabItem Header="One"
x:Name="tabOne">
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Set Focus"
Click="Button_Click" />
</TabItem>
<TabItem Header="Two"
Name="tabTwo">
<StackPanel>
<TextBox Name="txtOne" />
<TextBox Name="txtTwo" />
</StackPanel>
</TabItem>
<TabItem Header="Three"
x:Name="tabThree">
<StackPanel>
<TextBox Name="txtThree" />
<TextBox Name="txtFour" />
</StackPanel>
</TabItem>
</TabControl>
CODE
private void Button_Click(object sender, RoutedEventArgs e)
{
tabTwo.IsSelected = true;
FocusManager.SetFocusedElement(tabTwo, txtTwo);
}

Related

Extended WPF Toolkit - ChildWindow do not allow focus parent

I have a ChildWindow that overlays a view that has inputs and buttons. If the ChildWindow is opened i am not able to click any of the buttons which is good. But with TAB i am able to switch the focus and select one of the buttons and hit enter.
Any idea how I can prevent this?
Any help is appreciated...
The code is really simple and nothing special...
MainWindow.xaml
<Grid>
<xctk:ChildWindow Name="chWindow" IsModal="True" WindowStartupLocation="Center">
<TextBlock Text="Hello World ..." />
</xctk:ChildWindow>
<StackPanel>
<Button Content="Load" Click="OnLoadClicked"/>
<Button Content="Re-Initialize" Click="OnInitClicked"/>
<Button Content="Modal dialog" Click="OnModalClicked" />
<ListBox x:Name="listBox" />
</StackPanel>
</Grid>
MainWindow.xaml.cs
public MainWindow(IModuleCatalog moduleCatalog, IModuleManager moduleManager)
{
InitializeComponent();
this.DataContext = this;
this.ConfirmationRequest = new InteractionRequest<IConfirmation>();
this.moduleCatalog = moduleCatalog;
this.moduleManager = moduleManager;
UpdateModulesList();
this.Loaded += (s, e) => this.chWindow.Show();
}
Update: try setting KeyboardNavigation.TabNavigation to Cycle on the child window; this should be a much better solution.
You could try handling the PreviewLostKeyboardFocus event and simply not allowing it through; this should keep focus in your child window.

How to prevent a UserControl from taking focus away from its parent?

I am using MVVM for my application and have a form that allows the user to enter basic personnel information. The form includes a UserControl which is, basically, an ItemsControl that includes textBoxes that can be dynamically created. This is a simplified version:
<ItemsControl x:Name="items" ItemsSource="{Binding MyItemsCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="row">
<TextBox x:Name="textBox" Text="{Binding ContactInfo, ValidatesOnExceptions=True}" extensions:FocusExtension.IsFocused="{Binding IsFocused}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button x:Name="NewItemButton" Command="{Binding AddItemToMyCollectionCommand}" />
I want the TextBox that has just been created to receive focus, therefore I added an attached property. This is part of it:
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(FocusExtension), new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)d;
if ((bool)e.NewValue)
{
uie.Focus();
}
}
In the form that contains the UserControl there are several other text boxes before and after. The UserControl has its own ViewModel, which I set as the DataContext of the control through a property in the container's ViewModel. Basically, a simplified version of the container looks like this:
<StackPanel Orientation="Horizontal" />
<TextBox x:Name="firstName" />
<TextBox x:Name="lastName" />
<local:DynamicFormUserControl
x:Name="phones"
DataContext="{Binding PhonesViewModel}" />
<local:DynamicFormUserControl
x:Name="emails"
DataContext="{Binding EmailsViewModel}" />
<TextBox x:Name="address" />
</StackPanel>
My problem is that I want the firstName TextBox to get the focus when the form is loaded for the first time, but the form keeps on placing the focus on the first TextBox of the phones UserControl. I tried to override it by using firstName.Focus() on the Loaded event of the form, but this didn't work, and no matter what I tried the focus is still on the phones userControl instead of the first element in the form that contains it.
Does anybody have any idea how to solve this?
Thanks.
Here you go
add FocusManager.FocusedElement="{Binding ElementName=firstName}" to your stack panel
<StackPanel Orientation="Horizontal"
FocusManager.FocusedElement="{Binding ElementName=firstName}"/>
<TextBox x:Name="firstName" />
<TextBox x:Name="lastName" />
<local:DynamicFormUserControl
x:Name="phones"
DataContext="{Binding PhonesViewModel}" />
<local:DynamicFormUserControl
x:Name="emails"
DataContext="{Binding EmailsViewModel}" />
<TextBox x:Name="address" />
</StackPanel>
also notice that you may need to prevent items control in the user control from focusing itself
<ItemsControl x:Name="items" Focusable="False" >
<ItemsControl.ItemTemplate>
I guess I managed to find a solution. The problem was that the form I created was itself a user control inside a window, and never got focus. I didn't think that would be relevant so I didn't mention it in my previous post- sorry. I found in this solution for forcing focus to a user control.
Basically, when I have a UserControl inside a window it doesn't get focus even if I try to set the focus with either Focus() or FocusedElement. So to overcome this problem I found on a different post a workaround. Basically I added it to the code-behind of the UserControl that contains the firstName TextBox. If we call the UserControl, say, PersonalInfoUserControl, the constructor of the control would look like this:
public PersonalInfoUserControl()
{
InitializeComponent();
this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(UserControl_IsVisibleChanged);
}
I added an event handler to the IsVisibleChanged event of the control. The method would look like this:
void UserControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
firstName.Focus();
}));
}
}

Is this concept of a button containing a textbox possible?

Recently I had been looking for a way to make the tabs in a TabControl editable and came across This example on telerik's website. That did exactly what I wanted but it got me thinking about a similar usage for buttons. I was wondering if it would be possible to use something like that and make a button that would show a textbox instead of the content presenter when say, you right click the button? I tried to make something like this work but so far have only ended up with a blank button.
<Button x:Name="SB" Height="222" Width="222" Click="SB_Click">
<ContentControl.ContentTemplate>
<DataTemplate>
<local:SuperButton Content="{Binding Path=x, Mode=TwoWay}"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</Button>
Where x is a string variable and using the code behind from the link above (with a class name change, of course).
edit: This button will be in an itemscontrol, so I don't think naming the inner elements in xaml will work, but I do like the ease of Wolfgang's answer.
The WPF Content Model is really flexible and allows literally anything inside anything.
This is perfectly valid XAML:
<Button>
<TextBox/>
</Button>
Or even:
<Button>
<MediaElement Source="C:\Videos\WildLife.wmv"/>
</Button>
You can simply host a (e.g.) label (TextBlock) with the text AND a TextBox inside the Button and set their Visiblity properties.
That way, if you right click the button, the TextBox shows up.
<Button>
<Grid>
<TextBox Text=normal button caption" x:Name="label" />
<TextBox
x:Name="textbox"
Text="visible on right click"
MouseRightButtonDown="HandleRightClick"/>
</Grid>
</Button>
And then in your C# code create an event handler to set the Visiblity correctly.
void HandleRightClick(object sender, MouseButtonEventArgs e)
{
label.Visibility = Visibility.Collapsed;
textBlock.Visibility = Visibility.Visible;
}

Xaml TextBox not behaving

I am trying to create UI from XAML at runtime, with something like
private void Application_Startup (object esender, StartupEventArgs e)
{
this.RootVisual = (UIElement)(XmlReader.Load(e.InitParams["Xaml"])
If I feed it this XAML:
<Canvas
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls">
<StackPanel>
<TextBox Width="120" Margin="8" Text="Edit Me" />
<CheckBox Margin="8">Normal</CheckBox>
<ComboBox Margin="8" Width="120" SelectedIndex="1">
<ComboBoxItem Content="First Normal Item" />
<ComboBoxItem Content="Second Normal Item" />
</ComboBox>
</StackPanel>
</Canvas>
then the check box and list behave as expected, but my TextBox does not respond to typing, it stays with its initial value.
The really weird (to me) part is that if I put a handler for KeyDown on to the RootVisual, and in there display a message box, it works. If I have an empty handler or no handler it doesn't.
Do I need to set up some explicit handling for some events? If so, which ones, and how do I handle them?
Upadate: as suggested, I tried putting the dynamic markup into the MainPage of a new app, like this:
public MainPage()
{
InitializeComponent();
var dynamic = XamlReader.Load(xaml);
this.LayoutRoot.Children.Add(dynamic as UIElement);
}
where xaml is a string literal containing the content as above, and everything else is just how VS2010 wizard left it. That works. But I can't see what the effective difference is.
Update update: that's a red herring; the difference is the environment. It works in VS, but not in the Silverlight ActiveX control that I am using in the real app.
Did you define the root namespace on your root element?
<param name="initParams" value="xaml=<TextBox xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' Text='hi'/>" />
Just a shot in the dark here, but have you tried adding the dynamically created content as the child of a static "MainPage.xaml" Grid instead of as RootVisual?
Check for IsEnabled="True" property in your main XAML file, if it is set to false then controls will not be editable.

Getting events from the scrollbar buttons?

how can I access the arrow buttons on the ends of scrollbars in silverlight? Are they button controls which can be accessesed through thescrollbar class or something else?
The reason I need to know this is so that when someone click on either of the arrow buttons I can run some custom functionality to the scrollbar
If you need to know what button of the scrollbar was clicked you can could access that via the ValueChanged property of the ScrollBar
For instance I've got a simple scroll bar with a textblock and on each click, the textblock displays which button was clicked.
<Grid x:Name="LayoutRoot" Background="White">
<ScrollBar Height="200" Orientation="Vertical" Width="20" ValueChanged="ScrollBar_ValueChanged" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="150,21,0,0" Name="textBlock1" Text="" Width="100" VerticalAlignment="Top" />
</Grid>
and the code behind would be
private void ScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (e.NewValue > e.OldValue)
textBlock1.Text = "Down Arrow Clicked";
else
textBlock1.Text = "Up Arrow Clicked";
}
Solved this problem like so:
foreach( var o in horizontalBar.GetVisualDescendants( ) )
{
if(o is RepeatButton)
{
//set call back based on the name of the repeatbutton
}
}

Categories