WPF - Detect Explicit Tab Selection vs. programmatic Tab Selection - wpf

I have an application where I would like to explicitly set focus to particular content inside of TabItem Content, dependent on whether the user clicked the tab explicitly or whether the tab was activated via code by setting hte SelectedIndex. Specifically I don't want to set focus to the embedded document content when programmatically selected (as I can explicitly force it via code), but I do want to set it when activated via Tab header click.
I haven't been able to effectively intercept the tab header click operation. Tab and tab content container clicks don't seem to fire and inside of the SelectedIndex_Changed event there's no indication where the activation originated from.
Any ideas what I can look at to determine explicit manual vs. programmatic tab activation?

I had a solution for this. You can capture mouse click event on tab header and set a bool flag. Then check the same flag in 'SelectionChanged' event of tab control, you can do what you want here such setting focus, then reset the flag to identify further clicks. Here is the sample code.
<TabControl SelectionChanged="TabControl_SelectionChanged">
<TabItem>
<TabItem.Header>
<TextBlock Text="ABC" MouseDown="TextBlock_MouseDown"/>
</TabItem.Header>
<!--<TabItem.InputBindings>
<MouseBinding Gesture="LeftClick" />
</TabItem.InputBindings>-->
</TabItem>
<TabItem Header="XYZ" />
</TabControl>
In code behind you can check like this
private void TabControl_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if(isClicked)
{
//you can set focus here
isClicked = false;
}
}
private void TextBlock_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
isClicked = true;
}
You can also try input bindings for tab item to detect the mouse click and raise the command, if you want to set the variable or take any required action. This way will be useful if you are following MVVM

Related

WPF: Binding RadioButton 'IsChecked' to Settings.Default always setting to 'false'

I have been reading up on how to bind the 'IsChecked' property of a RadioButton to a boolean setting in Settings.Default (all in XAML). Now what I want to achieve is that everytime a radiobutton is chcked, the corresponding setting gets updated and saved. My code looks like this:
Global resource to access the application settings in App.xaml:
xmlns:properties="clr-namespace:MyApp.Properties"
<Application.Resources>
<ResourceDictionary>
<properties:Settings x:Key="Settings" />
</ResourceDictionary>
</Application.Resources>
Then I have a settings page with 2 RadioButtons for the boolean setting 'IsShortProgramFlow' ("On" and "Off").
The radiobuttons are declared like this:
<RadioButton Name="rbShortProgramFlowNo" Content="Off" GroupName="programFlow"> </RadioButton>
<RadioButton Name="rbShortProgramFlowYes" IsChecked="{Binding Source={StaticResource Settings}, Path=Default.IsShortProgramFlow, Mode=TwoWay}" Content="On" GroupName="programFlow"></RadioButton>
As you can see, the first radiobutton doesn't have a binding as it makes things even worse. Also, I am sure that the binding path is correct and thus the settings are accessed correctly.
In my code-behind, I registered the 'Settings.Default.PropertyChanged' to implement auto-save functionality.
Constructor of the page:
Settings.Default.PropertyChanged -= Default_PropertyChanged;
Settings.Default.PropertyChanged += Default_PropertyChanged;
Method:
private void Default_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (isInitialisation)
return;
Settings.Default.Save();
}
Now the problem: the setting is always set to 'false' as soon as the page is opened, closed and opened again. So when the setting is set to 'true' and I open the page, the radiobutton is checked as it should be. But when I close the page and open it again (via Frame Navigation), the setting is somehow set to "false" without any user interaction and the radiobutton is not checked.
I am sure that no other code part is accessing the setting, so what could be the cause for this behavior?
I believe that the cause of the described behavior is that the page, after it is closed, never gets garbage collected. So if multiple instances of the page access the Settings.Default via Bindings, something goes wrong.
My working solution is to do some manual "garbage collecting" in the Page_Unloaded event:
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
//stop anything that might keep the page alive
Settings.Default.PropertyChanged -= Default_PropertyChanged;
someDispatcherTimer.Stop();
//clear the checkbox bindings to keep the binding working when the page is loaded again
UiHelper.FindVisualChildren<RadioButton>(settingsTabControl).ToList().ForEach(rb => BindingOperations.ClearBinding(rb, RadioButton.IsCheckedProperty));
}
Note that UiHelper.FindVisualChildren() returns a list of all radiobuttons with a binding on my page.

Silverlight ListBox - Change When Selected State Occurs

I have a ListBox that is used as a navigation menu. When an item is selected, it has a state that is highlighted. I have now implemented a message box when navigating away from a page if there are unsaved changes. The problem is, the visual state of the ListBoxItem is changed to selected upon click. I need to be able to change set the state to selected from code, instead of on click.
Is there a way to override the click event so that it doesn't cause the ListBoxItem to go to the selected state? I could then do VisualStateManager.GoToState(item, "Selected", true).
If not, is there a way to create a custom visual state for the ListBoxItem?
You should interrupt routing of MouseLeftButtonDown event from your item container and set selected item from view model. For instance:
XAML
<ListBox x:Name="lb">
<ListBoxItem>
<TextBlock MouseLeftButtonDown="TextBlock_OnMouseLeftButtonDown" Text="Test"/>
</ListBoxItem>
</ListBox>
Event Handler
private void TextBlock_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//interrups item selection
e.Handled = true;
//here you can show "Do you want navigate from?" dialog
// and if user accepts then show selected item in menu using SelectedItem or SelectedIndex
lb.SelectedIndex = 0;
}

How to disable hide/unhide in WPF Ribbon Control

I am using the latest Ribbon Control from MS. When one double click the Tab Header, the whole Ribbon will hide the content and show only the header text. I want to disable this hide/unhide feature. So even if user double click Tab Header, the Ribbon remain as it is.
I think it should be the OnMouseDoubleClick Event in RibbonTab but have no clue how to override it. Am I suppose to give it a x:Name to each and every RibbonTab then write an empty method ribbonTab1_OnMouseDoubleClick to each Ribbon Tab?
Use the PreviewMouseDoubleClick event in the Ribbon:
PreviewMouseDoubleClick="ribbon_PreviewMouseDoubleClick"
and then in the handler, mark the event as handled:
private void ribbon_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}

focus does not enter targeted user control properly

I've got a user control, consisting of a text box and two buttons.
the control is placed on a dialog box and when i tab over the dialog
controls, I experience proper behavior - first the text box is focused,
then one button, then the other.
However, when I set the user control as a target of a keyboard shortcut
set with "_" for a label (say press alt+r for "_Row count") the user control
does not receive any focus. Tried implementing "gotkeyboardfocus" and setting
the focus to the textbox control there but it doesn't work.
A UserControl isn't focusable by default so you have to turn it on in order to get this to work.
<my:UserControl1 x:Name="userControl11" Focusable="True" .../>
<Label Target="userControl11">_Row count</Label>
And then you can focus the desired TextBox inside of the UserControl when it recieves Focus
private void UserControl_GotFocus(object sender, RoutedEventArgs e)
{
textBox.Focus();
}
Hi I recently encountered focus problem. What I did is create a method called SetFocus() from inside my user control. Then inside that usercontrol I set the focus directly to my textbox control after calling .SetFocus() from consumer.
public void SetFocus()
{
this.txtCommand.Focus();
}

Programmatically set content in Silverlight DataGrid details

I need to dynamically set the contents within the template of a DataGrid based on information in an external settings file. That settings file specifies which data fields should display in the DataGrid. The administrator of the application can edit the settings to change the fields to display. I cannot hard-code the fields to display.
I can easily add the columns (DataGridTextColumn's) to the DataGrid at runtime. I set a binding to a field in the item source based on the settings, and that displays fine.
Now I need to display details when the user clicks a row. I set up a RowDetailsTemplate with DataTemplate, and added a Grid (or a StackPanel) inside to format the details. If I add to the markup the TextBlocks with bindings to fields, it displays the details just fine.
But how can I set the content of the Grid/StackPanel in the details template programmatically? The Grid/StackPanel controls are null if I try to reference them by name on startup (e.g., in the page Loaded event). I have tried using the Loaded event on the Grid/StackPanel to add the details. That code runs and appears to add the content to the Grid/StackPanel, but nothing actually appears when I click the row. I'm guessing that the problem is that the template/Grid is already loaded and ignores the changes I'm making.
Here's a sample of the code I'm using in the handler for the Loaded event. Even if I do something as simple as this, the details pane doesn't appear when clicking on the row.
<data:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Border Background="LightBlue" >
<StackPanel x:Name="resultsDetailsPanel"
Orientation="Vertical"
Loaded="resultsDetailsPanel_Loaded">
</StackPanel>
</Border>
</DataTemplate>
</data:DataGrid.RowDetailsTemplate>
private void resultsDetailsPanel_Loaded(object sender, RoutedEventArgs e)
{
if (_resultsGridLoaded)
return;
StackPanel detailsPanel = sender as StackPanel;
TextBlock fieldNameTextBlock = new TextBlock();
fieldNameTextBlock.Text = "TESTING";
detailsPanel.Children.Add(fieldNameTextBlock);
_resultsGridLoaded = true;
}
I actually tried your code and is working. Two things you should check:
Is your _resultsGridLoaded variable initialized as false?
Did you set RowDetailsVisibilityMode="VisibleWhenSelected" on your DataGrid?
UPDATE: For some reason is not working anymore. But I did found two ways you can fix it:
Remove the resultsGridLoaded logic.
If you need that logic, you can add a handler for the SelectionChanged event on the DataGrid, in there you can set the _resultsGridLoaded variable to false so the new StackPanel gets its content added correctly:
And the code behind:
private void resultsPanel_Loaded(object sender, RoutedEventArgs e)
{
if (_resultsGridLoaded)
return;
StackPanel pane = (StackPanel)sender;
TextBlock newChild = new TextBlock()
{
Text = "New text"
};
pane.Children.Add(newChild);
_resultsGridLoaded = true;
}
private void grid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_resultsGridLoaded = false;
}
Hope this helps

Resources