WPF Setting focus Issues - c#

Basis: I am using the MVVM pattern or a subset of it.
In my main window I have a button that opens up a usercontrol with a new DataContext the function looks kinda like this:
public void SetUserControl()
{
UCDatacontext = new UCViewModel(this);
base.OnPropertyChanged("UCDatacontext");
UCViewVisibilty = Visibility.Visible;
UCDatacontext.IniFocus();
}
And then when I am done I close the usercontrol and Dispose of the DataContext. Now the problem I am having is that I can't seem to get the focus setting to work properly, I have a Textbox in the usercontrol that I want to set focus to when the view becomes Visible. However on the first time that I attempt to set focus it only fills the text box with an unblinking caret, which after investigation leads me to believe that it is because the TB isn't getting the Keyboard focus (only logical focus), however even after explicity setting the keyboard focus I still get the unblinking caret, and it is only after clicking in the TB that it is getting focus. The method I am using to set focus is similar to method described here.
If in the view I do some writeline debugging by printing out in the FocusSet Event for the textbox it does get set, however only on the first time I call SetUserControl(). If I call SetUserControl() again it does nothing, except making the View Visible but doesn't trigger the Focus Set Event.
Below is the lines of code from the MainWindow:
<Grid Grid.ColumnSpan="5" Grid.RowSpan="5" Visibility="{Binding Path=UCViewVisibilty }" x:Name="UCGrid" >
<Grid.Effect>
<DropShadowEffect />
</Grid.Effect>
<View:UCView DataContext="{Binding Path=UCDatacontext}" />
</Grid>
And UserControl Grid:
<Grid >
<TextBox Uid="UCTB" localExtensions:FocusExtension.IsFocused="{Binding Path=UCTBFocus}" Height="23" HorizontalAlignment="Left" Margin="113,56,0,0" Name="UCTB" VerticalAlignment="Top" Width="165" Text="{Binding Path=UCTBContent, UpdateSourceTrigger=PropertyChanged}" GotFocus="UCTB_GotFocus" />
</Grid >
The Focus is set in the UserControlViewModel, and is set after the Usercontrol is rendered.

it like this Set focus one by one from top to bottom.
InitializeComponent();
FocusManager.SetFocusedElement(this, TabItem); //this is Window , TabItem is UserControl in this Window
FocusManager.SetFocusedElement(TabItem, TextBox); // TabItem is UserControl and TextBox is Control in TabItem UC
I hope this will help.

As it turns out, after fiddling around with the code, the reason why the focus wasn't being set properly in the View was because the binding in the View Model was this:
bool _tBfocus;
public bool UCTBFocus
{
get { return _tBfocus; }
set
{
_tBfocus= value;
base.OnPropertyChanged("UCTBFocus");
}
instead of:
bool _tBfocus;
public bool UCTBFocus
{
get { return _tBfocus; }
set
{
if (_tBfocus == value)
return;
_tBfocus= value;
base.OnPropertyChanged("UCTBFocus");
}
}
After changing it everything worked fine :/ but if someone could explain to me why this annoyance I was having was caused by that I would be truly grateful :)

Related

TabItem OnSelectionChanged() setting focus on inner control (WPF)

I have two TabItem's contained inside a TabControl.
Each TabItem contains serveral TextBox's.
When TabControl's OnSelectionChanged event is fired, as well as selecting the new TabItem, it is also setting focus on the first TextBox contained inside the newly selected item.
Is there any way to prevent this from happening?
Setting IsTabStop="False" on the TextBox will achieve this, but unfortunately also prevents the TextBox from being 'tabbed' into.
In your tab control, handle the focus event for each of the tabs like this:
<TabItem GotFocus="TabItem_OnGotFocus">
Then just remove focus using:
private void TabItem_OnGotFocus(object sender, RoutedEventArgs e)
{
Keyboard.ClearFocus();
}
Just add a container to your content as Grid, Stackpanel, Border, etc. and set it Focusable. When Tab selection change, the focus is set to the container and you can also use the tab key.
<TabItem Header="myHeader">
<StackPanel Focusable="True">
...
</StackPanel>
</TabItem>
#shannon it answers to your question about MVVM

WPF popup wont close even with StaysOpen="false"

I am trying to achieve the following:
User brings up a context menu in a datagrid.
User selects a context menu item which then opens a popup and displays some information.
when the user clicks anywhere else in the application, not in the popup, the popup closes.
Everything works fine until I come to closing the popup.
From searching elsewhere I am aware that I need Staysopen to be set to false ( which it is)
I also read the best way is to bind the IsOpen value to a property in the view model and set its binding to 2 way ( also done )
As a side note I have found that if I add a textbox and click inside the box, when I then click outside the popup it closes as desired.
Another thing I unsuccessfully tried as a workaround was to programmatically set the keyboard focus on the text box to get the "autoclose" functionality I desired.
Here is code:
xaml -
<Popup Name="PredictionsPopup" Height="200" Width="200" AllowsTransparency="false" StaysOpen="False" IsOpen="{Binding DisplaySummaryPopup, Mode=TwoWay}">
<StackPanel Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
<TextBlock Text="here is some stuff" />
<TextBox Name="hiddenBox" Text="moo"/>
</StackPanel>
</Popup>
Codebehind that sets the property on the viewmodel when the menu item is selected.
private void CurrentPredicitions_OnClick(object sender, RadRoutedEventArgs e)
{
PredictionsPopup.Placement = PlacementMode.MousePoint;
ViewModel.DisplaySummaryPopup = true;
}
Viewmodel property
public bool? DisplaySummaryPopup
{
get
{
return this.displaySummaryPopup;
}
set
{
this.displaySummaryPopup = value;
RaisePropertyChanged(() => this.DisplaySummaryPopup);
}
}
Please let me know if you need anymore details.
Here you have a working example:
MainWindow XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Popup Name="PredictionsPopup" Height="200" Width="200" AllowsTransparency="false" StaysOpen="False" IsOpen="{Binding DisplaySummaryPopup, Mode=TwoWay}">
<StackPanel Background="Red">
<TextBlock Text="here is some stuff" />
<TextBox Name="hiddenBox" Text="moo"/>
</StackPanel>
</Popup>
<DataGrid AutoGenerateColumns="False" Name="dataGrid1" IsReadOnly="True" >
<DataGrid.Columns>
<DataGridTextColumn Header="Site" Width="150" />
<DataGridTextColumn Header="Subject" Width="310" />
</DataGrid.Columns>
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Click Me" Click="ButtonBase_OnClick">
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
</Window>
MainWindow cs :
public MainWindow()
{
InitializeComponent();
DataContext = new TestViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
PredictionsPopup.Placement = PlacementMode.MousePoint;
PredictionsPopup.IsOpen = true;
}
ViewModel:
public class TestViewModel : INotifyPropertyChanged
{
private bool _displaySumarry;
public bool DisplaySummaryPopup
{
get { return _displaySumarry; }
set
{
_displaySumarry = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I think that your implementation for INotifyPropertyChanged is the one which causes the problem. I tried myself the code and is working now.
After trying to track this problem down I worked out that the issue is something to do with the context menu. I know this because as per the answer above instead of launching my popup via a context menu I launched it from a test button and all worked as expected.
I still don't know the exact reason for this issue but I think its something to do with the fact that the context menu is itself a subclass of popup and the focus isn't being set correctly on the new popup, so it never detects popup loss and closes.
To get round my problem I have added a close button to the popup, and then whenever the active tab in the control that hosts the popup changes it fires an event that the popup picks up and closes.
Had the same problem. The reason was, that the toggle button's ClickMode property was set to "Press". Setting it back to "Release" solved it :).
For me, the solution was to add this line in the constructor of the popup's code-behind:
LostFocus += delegate { this.IsOpen = false; };
Many hours were spent, when such a quickie line was all it took :)
I faced same problem a few times. Every time it was occuring, when a popup was changing its "isOpen" property to true from an event, which was raised from listview or datagrid element, like selectedItemChanged event, or items mouseUp event. I don't know reason, however resolved it by opening the popup from another task with code as below:
Task.Run(()=> Dispatcher.Invoke( () => myPopup.IsOpen = true));
Dispatcher is used to avoid an exception from changing any GUI object property from another than the main thread.

Working with elements inside a control that is bound to a collection

I have an ObservableCollection<string> that is bound to an ItemsControl whose template is just a Button. The content of this button are 2 TextBlock. I'm trying to use the PreviewMouseRightButtonUp event of the button to toggle the visibility of one of the textblocks, but without being able to use xaml names for elements in the template I'm hitting a wall. Is there a way of getting to the button's content elements via sender in that preview event, or some other way of doing this? This is related to a previous question I had that didn't quite get a usable answer (probably due to my explanation, hence this simplified example). It seems to me that what should happen is I should make a control based off button that adds a property for this toggle, but that is basically what I thought I had in the previous question that wasn't working. I feel like a property and trigger is what most would say is the right way to go?
xaml:
<ItemsControl x:Name="iC" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button PreviewMouseRightButtonUp="Button_PreviewMouseRightButtonUp">
<DockPanel>
<TextBlock Text="normal" DockPanel.Dock="Top"/>
<TextBlock Text="{Binding}" DockPanel.Dock="Top" Visibility="Collapsed"/>
</DockPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
code behind:
ObservableCollection<string> x = new ObservableCollection<string>();
public MainWindow()
{
x.Add("1");
x.Add("2");
InitializeComponent();
iC.ItemsSource = x;
}
If you name the hidden text block "secondTextBlock", then this should work:
private void Button_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
DockPanel dockPanel = (DockPanel)((Button)sender).Content;
TextBlock text = (TextBlock)LogicalTreeHelper.FindLogicalNode(dockPanel, "secondTextBlock");
if (text != null)
{
text.Visibility = Visibility.Visible;
}
}
Regarding your comment below: yes, multiple instances of "secondTextBlock" will be created. See the Snoop screenshot below. But these multiple instances are OK; they do not have any negative impact.

Silverlight app freezes when simply adding a textblock to the layoutroot

I cannot understand why in the hell this simple silverlight application freezes up. Here is the code:
namespace test
{
public partial class MainPage : UserControl
{
TextBlock txtword;
public MainPage()
{
InitializeComponent();
txtword = new TextBlock();
txtword.Text = "TEST";
LayoutRoot.Children.Add(txtword);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
txtword.Text = "SuperDuper";
}
}
}
After the textblock is added to the layoutroot if you try to hover or click on the button you can tell that the app has frozen for some reason. Any idea what is going on here??
If i add the text block in the XAML i am able to change its text property in the button click. LayoutRoot.Children.Add() is causing the app to freeze..
From reading your comments it seems the XAML in MainPage.xaml is something like the following:
<Grid x:Name="LayoutRoot" Background="White">
<Button Content="Do stuff" Click="Button_Click" />
</Grid>
After adding the TextBlock, either in code or in XAML, you effectively end up with the following:
<Grid x:Name="LayoutRoot" Background="White">
<Button Content="Do stuff" Click="Button_Click" />
<TextBlock Text="TEST" />
</Grid>
Your Grid doesn't specify any ColumnDefinitions or RowDefinitions, so you have a 1 × 1 grid with all child controls of the Grid given the entire width and height of the grid.
As neither your Button nor your TextBlock specify a z-index value (using Canvas.ZIndex), their z-order is defined by their position within the grid's Children. The TextBlock comes after the Button, so it is the one that is 'on top'.
The TextBlock may contain only a tiny amount of text, but the TextBlock itself will still fill the Grid. TextBlocks do not automatically resize to fit the text they contain and nothing else. Your Button appears not to work because the TextBlock is on top of it and receives all of the mouse events. TextBlocks are static controls that do nothing in response to any mouse event, and this should explain why your app is appearing to freeze.
Setting the HorizontalAlignment and/or VerticalAlignment of the TextBlock to a value other than Stretch stops the TextBlock being given the entire width and height of the Grid and allows the Button to receive mouse events.

TabItem gets blocked permanently

I am experiencing a very weird problem: In WPF I have a tabControl which contains 3 tabItems. I have made a binding from the tabControl's SelectedIndex to a property inside my viewModel class in "OneWayToSource" mode.
Here's the XAML code:
<TabControl Name="mainTabControl" SelectedIndex="{Binding SelectedTabIndex, Mode=OneWayToSource}" >
<TabItem Header="Tab 01" Name="tab01"> ... </TabItem>
<TabItem Header="Tab 02" Name="tab02"> ... </TabItem>
<TabItem Header="Tab 03" Name="tab03"> ... </TabItem>
</TabControl>
And in my viewModel:
private int m_selectedTabIndex;
public int SelectedTabIndex
{
get
{ return m_selectedTabIndex; }
set
{
SetAndNotify(ref m_selectedTabIndex, value, () => SelectedTabIndex);
SelectedTabChanged();
}
}
private void SelectedTabChanged()
{
// Some code
}
As you can see, everytime my viewModel's SelectedTabIndex property changes, the SelectedTabChanged() method is executed, this works perfectly.
My weird problem is that: When I show a message using for example System.Windows.MessageBox.Show("Some Text") inside my SelectedTabChanged() method, I select another tabItem and the previous selected tab gets blocked, it looks like selected, but it remains selected permanently, I cannot see its content anymore.
Just to clarify: As I stated before, this weird issue only happens when a modal window is showed
Why is happening? How can I solve this issue?
I hope I explained myself clearly.
Thank you in advance.
I have solved my problem. Since I am new in WPF I really dont understand why a modal window make the tabs get blocked. But I was searching and found that the Dispatcher class allows one to execute asynchronously a method which prevent any control from get blocked.
I changed my viewModel code as below:
public int SelectedTabIndex
{
get
{ return m_selectedTabIndex; }
set
{
SetAndNotify(ref m_selectedTabIndex, value, () => SelectedTabIndex);
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(SelectedTabChanged), null);
}
}
The line that really helped me was the following:
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(SelectedTabChanged), null);
Hope this help someone else can be experiencing some similar problem.
Im not 100% sure I understand your problem, but a MessageBox.Show will spawn a nested message pump and block the main Dispatcher, thus 'freezing' the main window (which is why it is modal).
If you need to show a MessageBox style alert without it being modal, then my advice is to create a panel with your message in it that exists in the TabItem with a hidden visibility, then make it visible when required to show you message. Your Tab selections should still work in that scenario

Categories