Empty textbox on mousclick in combination with language change in WPF - wpf

I have a textbox with a default text. When I focus the textbox it is cleared so I can write, and if I unfocus without writing anything the default text reappears.
I also have two radiobuttons for selecting the language. The languages are provided as xaml resourcefiles and the default text in the textbox is connected to that using DynamicResource.
My problem is that the language change only work as long as I haven't focused the textbox. If I focus the textbox and then unfocus it without changing anything, the textbox no longer changes language.
I'm guessing that is because once it's changed (cleared) it's no longer linked to the dynamic resource, because WPF considers my onfocus changes as user input, but I can't figure out how to get around that and make it change language even if I've clicked the textbox.
The second textbox don't have any focus behaviour and in that one the language change works as it should, i.e. it changes the language as long as I haven't actually written something.
MainWindow xaml:
<Window x:Class="Textbox_langauge_buggseek.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Textbox_langauge_buggseek"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox x:Name="TextBox" HorizontalAlignment="Left" Height="46" Margin="84,55,0,0" TextWrapping="Wrap" Text="{DynamicResource ResourceKey=TB}" VerticalAlignment="Top" Width="334" GotFocus="TextBox_GotFocus" LostFocus="TextBox_LostFocus"/>
<TextBox x:Name="TextBox_Copy" HorizontalAlignment="Left" Height="46" Margin="84,123,0,0" TextWrapping="Wrap" Text="{DynamicResource ResourceKey=TB}" VerticalAlignment="Top" Width="334"/>
<RadioButton x:Name="En" Content="En" GroupName="Lang" HorizontalAlignment="Left" Margin="391,216,0,0" VerticalAlignment="Top" Checked="En_Checked" IsChecked="True"/>
<RadioButton x:Name="Se" Content="Se" GroupName="Lang" HorizontalAlignment="Left" Margin="391,234,0,0" VerticalAlignment="Top" Checked="Se_Checked"/>
</Grid>
</Window>
MainWindows cs:
using System;
using System.Windows;
using System.Windows.Controls;
namespace Textbox_langauge_buggseek
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SetLanguageDictionary();
}
//*****************************************************************************************
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
TextBox box = sender as TextBox;
box.Text = box.Text == (string)this.Resources["TB"] ? string.Empty : box.Text;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
TextBox box = sender as TextBox;
box.Text = box.Text == string.Empty ? (string)this.Resources["TB"] : box.Text;
}
//*****************************************************************************************
private void En_Checked(object sender, RoutedEventArgs e)
{
SetLanguageDictionary("En");
}
private void Se_Checked(object sender, RoutedEventArgs e)
{
SetLanguageDictionary("Se");
}
//*****************************************************************************************
private void SetLanguageDictionary(string language = "En")
{
ResourceDictionary dict = new ResourceDictionary();
switch (language)
{
case "Se":
dict.Source = new Uri("..\\Resources\\Se.xaml", UriKind.Relative);
break;
default:
dict.Source = new Uri("..\\Resources\\En.xaml", UriKind.Relative);
break;
}
this.Resources.MergedDictionaries.Add(dict);
}
}
}
En language xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="TB">Text in the TextBox!</system:String>
</ResourceDictionary>
Se language xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="TB">Text i textrutan!</system:String>
</ResourceDictionary>

Yes, when you set TextBox.Text in codebehind, the text does not know anymore that it has to take a value from the Rersource. To avoid it you can change the text using pure XAML, with Triggers.
Remove TextBox's event handlers and add a style like this:
<TextBox x:Name="TextBox" HorizontalAlignment="Left" Height="46" Margin="84,55,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="334">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="Text" Value="" />
</Trigger>
<Trigger Property="IsFocused" Value="false">
<Setter Property="Text" Value="{DynamicResource ResourceKey=TB}" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

Related

Handle Events in Custom Control Code Behind

Ok, that probably is a pretty dumb question but I have searched quite a while but could not find a solution for this that works...
I have a Custom control inherited from Control, which shall include code behind automation.
For examble select all text of a controls TextBox when selected, or generate a list of close matches when the content of that TextBox is changed.
public class vokDataGridEdit : Control
{
static vokDataGridEdit()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(vokDataGridEdit), new FrameworkPropertyMetadata(typeof(vokDataGridEdit)));
// Events internal to control (??? found on some how-to's)
EventManager.RegisterClassHandler(typeof(vokDataGridEdit), UIElement.GotKeyboardFocusEvent, new RoutedEventHandler(OnSelectContent), true);
}
// Dependecy Properties ...
// The Event that shall Fire when the TextBox gets Focus / Editing Mode
public static void SelectContent(object sender, RoutedEventArgs e)
{
if (sender is TextBox tb)
{
tb.SelectAll();
}
}
}
And the controls Style Template:
<ResourceDictionary xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ccont = "clr-namespace:App.Controls">
<!-- Default style for the Validation Buttons -->
<Style TargetType="{x:Type ccont:vokDataGridEdit}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ccont:vokDataGridEdit}">
<TextBox Text = "{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}"
BorderThickness = "0"
ContextMenuService.Placement = "Right"
ContextMenuService.PlacementTarget = "{Binding Path=., RelativeSource={RelativeSource Self}}"
GotKeyboardFocus = "SelectContent">
<TextBox.ContextMenu>
<ContextMenu>
<ContextMenu.Template>
<ControlTemplate>
<Border CornerRadius = "5"
Background = "LightGray"
BorderThickness = "1"
BorderBrush = "Gray"
Padding = "2">
<StackPanel Orientation="Vertical">
<!-- Title -->
<TextBlock Text="Test" />
<!-- TODO: List of matches -->
<TextBox Text = "{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}"
BorderThickness = "0" />
</StackPanel>
</Border>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Question 1: How can I bind the event SelectContent (to select all TextBox content when it get focus, nb: it is part of a DataGrid for the CellEditingTemplate) to GotKeyboardFocus? Events are normaly fine in the Apps code, but for the Custom Control they do not work as there is no "Code Behind" really for the Style...
Question 2: Assuming I have a dependency Property containing an array of words. Based on the content of the TextBox, I would like to select a few words from the Array in the Dependency Property and pass them to a ListBox in the Custom Control (the Content of the ListBox shall only be managed by the Custom Control, not by anyone using that control. Is there a prefered/canonical MVVM schema on how to implement this?
Usually you should post only one question, not multiple. Regarding first one you can use EventSetter e.g. in implicit Style in UserControl's resources:
<Style TargetType="TextBox">
<EventSetter Event="GotKeyboardFocus" Handler="SelectContent"/>
</Style>
Regarding second question - implement a property, which is subset of your list and do update it accordingly e.g. if dependency property was changed(see property changed callback) or some another values were changed which the subset depends on.
Alternatively you could use a behavior for the TextBox and handle events you need there. See e.g. select all behavior:
public class SelectAllBehavior : Behavior<TextBox>
{
private bool _doSelectAll = false;
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotFocus += AssociatedObject_GotFocus;
AssociatedObject.PreviewMouseUp += AssociatedObject_MouseUp;
AssociatedObject.PreviewMouseDown += AssociatedObject_MouseDown;
}
private void AssociatedObject_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (_doSelectAll)
{
AssociatedObject.Dispatcher.BeginInvoke((Action) (()=>{ AssociatedObject.SelectAll(); }));
}
_doSelectAll = false;
}
private void AssociatedObject_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_doSelectAll = !AssociatedObject.IsFocused;
}
private void AssociatedObject_GotFocus(object sender, System.Windows.RoutedEventArgs e)
{
AssociatedObject.SelectAll();
}
protected override void OnDetaching()
{
AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
AssociatedObject.PreviewMouseUp -= AssociatedObject_MouseUp;
AssociatedObject.PreviewMouseDown -= AssociatedObject_MouseDown;
base.OnDetaching();
}
}
Using this behavior in XAML:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<TextBox Text="Some text">
<i:Interaction.Behaviors>
<local:SelectAllBehavior/>
</i:Interaction.Behaviors>
</TextBox>
Partial Solution:
Finaly I got event on the direct controls to work (controls in a ContextMenu still don't get EventHandlers...).
Apparently the point was using GetTemplateChild() in order to get the TextBox by name, and then associate the Event handlers:
<ResourceDictionary xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ccont = "clr-namespace:App.Controls">
<!-- Default style for the Validation Buttons -->
<Style TargetType="{x:Type ccont:vokDataGridEdit}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ccont:vokDataGridEdit}">
<TextBox Text = "{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}"
BorderThickness = "0"
ContextMenuService.Placement = "Right"
ContextMenuService.PlacementTarget = "{Binding Path=., RelativeSource={RelativeSource Self}}"
x:Name = "TextBox">
<TextBox.ContextMenu>
<ContextMenu x:Name="Menu">
<ContextMenu.Template>
<ControlTemplate>
<Border CornerRadius = "5"
Background = "LightGray"
BorderThickness = "1"
BorderBrush = "Gray"
Padding = "2">
<StackPanel Orientation="Vertical">
<!-- Title -->
<TextBlock Text="Test" x:Name = "Test" />
<!-- TODO: List of matches -->
<TextBox Text = "{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}"
BorderThickness = "0" />
</StackPanel>
</Border>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And Code (Dependency Properties not shown):
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace App.Controls
{
/// <summary>
/// DataGrid Edit control (see: https://www.c-sharpcorner.com/article/wpf-routed-events/ for RoutedEvents)
/// </summary>
public class vokDataGridEdit : Control
{
static vokDataGridEdit()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(vokDataGridEdit), new FrameworkPropertyMetadata(typeof(vokDataGridEdit)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Demo purpose only, check for previous instances and remove the handler first
if (this.GetTemplateChild("TextBox") is TextBox button)
{
button.PreviewMouseLeftButtonDown += this.SelectContentPreparation;
button.GotKeyboardFocus += this.SelectContent;
button.MouseDoubleClick += this.SelectContent;
//button.GotFocus += this.SelectContent;
}
}
/// <summary>
/// Prepare the Control to ensure it has focus before subsequent event fire
/// </summary>
private void SelectContentPreparation(object sender, MouseButtonEventArgs e)
{
if (sender is TextBox tb)
{
if (!tb.IsKeyboardFocusWithin)
{
e.Handled = true;
tb.Focus();
}
}
}
private void SelectContent(object sender, RoutedEventArgs e)
{
if (sender is TextBox tb)
{
e.Handled = true;
tb.SelectAll();
}
}
}
}

Set focus to content of ContentPresenter

I need to set focus to the content of a ContentPresenter. I can assume the ContentTemplate contains an IInputElement but not anything else about it.
Here is a much simplified example that illustrates the problem:
Main window:
<Window x:Class="FiedControlTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:Esatto.Wpf.CustomControls;assembly=Esatto.Wpf.CustomControls"
xmlns:local="clr-namespace:FiedControlTest">
<Window.Resources>
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Margin" Value="5"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="LightBlue"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=Options}" Name="cbOptions" DisplayMemberPath="Description"/>
<Button Content="Set focus" Click="SetFocus"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="TextBox:"/>
<TextBox Name="tbText" Text="A bare text box."/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="ContentPresenter:"/>
<ContentPresenter Content="TextBox in a ContentPresenter" Name="cpText">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=OneWay}" IsReadOnly="True"/>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</StackPanel>
</StackPanel>
</Window>
Codebehind:
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = this;
Options = new ObservableCollection<Option>(new[]{
new Option(){TargetType=typeof(TextBox), Description="Bare Text Box"},
new Option(){TargetType=typeof(ContentPresenter), Description="Content Presenter"}
});
InitializeComponent();
cbOptions.SelectedIndex = 0;
}
private void SetFocus(object sender, RoutedEventArgs e)
{
var opt = cbOptions.SelectedItem as Option;
if (opt.TargetType == typeof(TextBox))
tbText.Focus();
if (opt.TargetType == typeof(ContentPresenter))
cpText.Focus();
}
public ObservableCollection<Option> Options { get; set; }
public class Option
{
public Type TargetType { get; set; }
public string Description { get; set; }
}
}
There's not much there. The bare TextBox takes focus as expected; the TextBox presented by the ContentPresenter does not.
I have tried adding Focusable="True" to the ContentPresenter but it doesn't have any visible effect. I've tried doing using Keyboard.SetFocus instead of UIElement.Focus but the behavior doesn't change.
How is this done?
In fact what you set focus is the ContentPresenter, not the inner TextBox. So you can use VisualTreeHelper to find the child visual element (the TextBox in this case) and set focus for it. However with IsReadOnly being true, you won't see any caret blinking (which may also be what you want). To show it in readonly mode, we can just set IsReadOnlyCaretVisible to true:
private void SetFocus(object sender, RoutedEventArgs e)
{
var opt = cbOptions.SelectedItem as Option;
if (opt.TargetType == typeof(TextBox))
tbText.Focus();
if (opt.TargetType == typeof(ContentPresenter)) {
var child = VisualTreeHelper.GetChild(cpText, 0) as TextBox;
if(child != null) child.Focus();
}
}
Here the edited XAML code with IsReadOnlyCaretVisible added:
<TextBox Text="{Binding Mode=OneWay}" IsReadOnly="True"
IsReadOnlyCaretVisible="True"/>
Note that the above code can only be applied in your specific case where you use a TextBox as the root visual of ContentTemplate of a ContentPresenter. In general case, you will need some recursive method to find the child visual (based on VisualTreeHelper), you can search more for this, I don't want to include it here because it's a very well-known problem/code in WPF to find visual child in WPF. (the italic phrase can be used as keywords to search for more).

Single Left Mouse Click on TreeView Node to open a document on a different User Control

I have Tree View whose information are filled with a structure of a document.
Every single article is represented by a single TreeView Node.
The goal is to raise the click event, pass the key that identifies that precise part of the document and render the information
I have 3 problems:
1) How can I pass the information to a different User Control
2) The Double click event works (just tried with a simple textbox) but not the single left click... :(
3) How can I open the precise part of the document I select on the treeview and repeat the operation. So e.g.: I click on the article number 3, I want the document of article 3 rendered, I click on article 5 etc. etc.
Code below:
<UserControl x:Class="UserControls.DocumentViewLaw"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="800" d:DesignWidth="900"
xmlns:controls="clr-namespace:Client.UserControls">
<Grid x:Name="grdTop">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="220"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.ColumnSpan="2">
<TreeView x:Name="treeViewStructure" HorizontalAlignment="Left" Width="200" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Border x:Name="bdrTreeViewItem" HorizontalAlignment="Right" BorderThickness="2" Margin="0.5" Padding="1">
<TreeViewItem Header="{Binding Text}" x:Name="treeViewItem" HorizontalAlignment="Left" HorizontalContentAlignment="Left">
</TreeViewItem>
</Border>
<HierarchicalDataTemplate.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="OnTreeNodeMouseClick" />
</Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="LightBlue" />
<Setter Property="BorderThickness" Value="3" />
<Setter Property="CornerRadius" Value="9" />
</Trigger>
</Style.Triggers>
</Style>
</HierarchicalDataTemplate.Resources>
<HierarchicalDataTemplate.Triggers>
<Trigger SourceName="treeViewItem" Property="IsMouseOver" Value="True">
<Setter TargetName="bdrTreeViewItem" Property="Background" Value="LightGray" />
<Setter TargetName="treeViewItem" Property="Foreground" Value="Red" />
</Trigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
<StackPanel Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<controls:TabDocumentViewLawControl x:Name="topTabLaw" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Row="0" />
</StackPanel>
</Grid>
</UserControl>
CodeBehind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Client.UserControls
{
public partial class DocumentViewLaw : UserControl
{
public DocumentViewLaw()
{
InitializeComponent();
}
public void SetTreeViewNodeStructure(IList<TreeViewNode> nodes)
{
//this method is recalled in MainWindow.cs where I pass the object returned by
// WCF and attached to the TreeView
this.treeViewStructure.ItemsSource = nodes;
}
public void OnTreeNodeMouseClick(object sender, RoutedEventArgs e)
{
}
}
}
Second User Control where to visualize the document:
<UserControl x:Class="Client.UserControls.TabDocumentViewLawControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:editor="clr-namespace:RichEditor;assembly=RichEditor"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="500"
xmlns:vm="clr-namespace:Domain.Model.Document;assembly=Domain">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<ScrollViewer Grid.Row="5" Grid.Column="1" MaxHeight="250">
<StackPanel>
<FlowDocumentReader x:Name="articoloDocumentLaw" Grid.Row="1" Document="{Binding Path=FlowDocumentArticle}"
Visibility="{Binding Path=HasArticoloVisible, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</ScrollViewer>
</UserControl>
The object that I pass to the UserControl to visualize the document and his structure in
"DocumentViewLaw" User Control is the single result of a result list
In MainWindow component I associate the data and correspondant context.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
this.login.btnLogin.Click += btnLogin_Click;
this.tabMainControl.resultListControl.RowSelected += resultListControl_RowSelected;
}
void resultListControl_RowSelected(object sender, EventArgs e)
{
AutonomySearchResult selectedDocument = (AutonomySearchResult)this.tabMainControl.resultListControl.grdResult.SelectedItem;
this.tabMainControl.topTabControl.SelectedItem = this.tabMainControl.tabResultList;
Services.ServicesClient client = new Services.ServicesClient();
var document = client.GetDocument(selectedDocument.DocKey, true);
this.tabMainControl.topTabControl.SelectedItem = this.tabMainControl.tabDocumentView;
this.tabMainControl.tabDocumentView.DataContext = document;
TreeViewFactory treeFactory = new TreeViewFactory();
var documentStructure= treeFactory.GetStructure(document.DocumentKey, document.XmlStructure, true);
this.tabMainControl.documentViewLaw.SetTreeViewNodeStructure(documentStructure);
}
public virtual void onResultClick(object sender, RoutedEventArgs e)
{
}
}
Factory of TreeView:
public class TreeViewFactory
{
public IList GetStructure(DocumentKey docKey, string structure, bool loadAllParents)
{
//business logic with LINQ2XML
}
public class TreeViewNode
{
public TreeViewNode() { }
public DocumentKey DocKey { get; set; }
public string Text { get; set; }
public IList<TreeViewNode> Children { get; set; }
}
Thank u very much in advance :)
1) How can I pass the information to a different User Control?
I assume that the articles data in the TreeView.ItemSource and the data in the second UserControl are bound from properties in a view model or class that is set as the DataContext of your Window or UserControl. From your view model, you can either bind to the SelectedItem property of the TreeView, or monitor the INotifyPropertyChanged interface to see when the property that is bound to the TreeView.ItemsSource is changed (by the user). At this point, you can load the required data and update whichever property is data bound to the second UserControl.
2) The Double click event works (just tried with a simple textbox) but not the single left click... :(
If you data bind as suggested above, then you will not need a Click handler, as you can find out when the user selects another node directly in the view model.
3) How can I open the precise part of the document I select on the treeview and repeat the operation. So e.g.: I click on the article number 3, I want the document of article 3 rendered, I click on article 5 etc. etc.
I don't really understand this part of your question. You should be able to access all of the properties of the selected item in the TreeView when binding to the view model.

Manipulation events not firing

I'm new to developing in WPF for touchscreens, and I'm having trouble interpreting manipulation events. What I want to do is fairly simple I believe: when the user pinches anywhere on a UserControl, it will perform an action.
So, in the control I have (this is Surface 2.0 / Windows Touch):
XAML
<Grid Background="White" IsManipulationEnabled="True"
ManipulationStarting="Grid_ManipulationStarting"
ManipulationDelta="Grid_ManipulationDelta">
<!--Some content controls-->
</Grid>
C#
private void Grid_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
{
e.ManipulationContainer = this;
}
private void Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
//do the thing... you know, that thing you do
}
However, neither of these events fire when I rub my hands all over the screen. I think I must not understand the routing of the event in this situation. My monitor (3M MicroTouch PX) has not had any trouble understanding touch events or built-in manipulation like in ScatterViewItems.
EDIT: I removed controls from inside the Grid and they fire now, so I guess manipulations are being intercepted by the content. Sorry, should have been more clear about the contents of the control, because they're the problem it seems.
Specifically I think it has to do with the fact that I have a SurfaceListBox inside. I would imagine the SurfaceListBox is intercepting manipulation. Is there a way I can tell it to step off? I'm still trying to wrap my head around the way WPF does events.
Edit2: Going to paste some more complete code.
SEMANTICPANEL.XAML FULL
<UserControl x:Class="SemanticZoom.SemanticPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SemanticZoom"
xmlns:views="clr-namespace:SemanticZoom.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
</UserControl.Resources>
<Grid Background="White" IsManipulationEnabled="True" ManipulationStarting="Grid_ManipulationStarting" ManipulationDelta="Grid_ManipulationDelta">
<views:CategoryView x:Name="CategoryView"/>
<views:ShelfView x:Name="ShelfView" Visibility="Hidden" />
<views:BookView x:Name="BookView" Visibility="Hidden" />
</Grid>
SEMANTICPANEL.CS FULL
public partial class SemanticPanel : UserControl
{
public SemanticPanel()
{
InitializeComponent();
CategoryView.CategorySelected += new EventHandler(CategoryView_CategorySelected);
ShelfView.BookSelected += new EventHandler(ShelfView_BookSelected);
ShelfView.ZoomOut += new EventHandler(View_ZoomOut);
}
void View_ZoomOut(object sender, EventArgs e)
{
if (sender == ShelfView)
{
ShelfView.Visibility = System.Windows.Visibility.Hidden;
CategoryView.Visibility = System.Windows.Visibility.Visible;
}
else if (sender == BookView)
{
BookView.Visibility = System.Windows.Visibility.Hidden;
ShelfView.Visibility = System.Windows.Visibility.Visible;
}
}
void ShelfView_BookSelected(object sender, EventArgs e)
{
BookView.Books = ShelfView.BookList;
ShelfView.Visibility = System.Windows.Visibility.Hidden;
BookView.Visibility = System.Windows.Visibility.Visible;
}
void CategoryView_CategorySelected(object sender, EventArgs e)
{
ShelfView.Category = CategoryView.ActiveCategory;
ShelfView.Visibility = System.Windows.Visibility.Visible;
CategoryView.Visibility = System.Windows.Visibility.Hidden;
ShelfView.RefreshBooks();
}
private void Grid_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
{
//e.ManipulationContainer = this;
}
private void Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
if (e.DeltaManipulation.Scale.X < 0)
{
if (ShelfView.Visibility == System.Windows.Visibility.Visible)
{
ShelfView.Visibility = System.Windows.Visibility.Hidden;
CategoryView.Visibility = System.Windows.Visibility.Visible;
}
else if (BookView.Visibility == System.Windows.Visibility.Visible)
{
BookView.Visibility = System.Windows.Visibility.Hidden;
ShelfView.Visibility = System.Windows.Visibility.Visible;
}
}
}
CATEGORYVIEW.XAML
<UserControl x:Class="SemanticZoom.Views.CategoryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="http://schemas.microsoft.com/surface/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<!--CATEGORY TEMPLATE-->
<DataTemplate x:Name="CategoryTemplate" x:Key="CategoryTemplate">
<s:SurfaceButton Background="Gray" Click="CategoryClicked" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Height="200" Width="300">
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" Foreground="White" FontSize="20" FontWeight="Bold" />
</s:SurfaceButton>
</DataTemplate>
<!--CATEGORY STYLE-->
<Style TargetType="{x:Type s:SurfaceListBoxItem}">
<Setter Property="Width" Value="300"/>
<Setter Property="Height" Value="200"/>
</Style>
</UserControl.Resources>
<Grid>
<s:SurfaceListBox x:Name="CategoryList" ItemTemplate="{StaticResource CategoryTemplate}">
<s:SurfaceListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"
Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType={x:Type ScrollContentPresenter}, Mode=FindAncestor}}"/>
</ItemsPanelTemplate>
</s:SurfaceListBox.ItemsPanel>
</s:SurfaceListBox>
</Grid>
Just don't judge me because a) yes I'm trying to emulate Semantic Zoom and b) yes I'm doing a hacky job of it. I just need a simple working concept. Each of the views is basically similar to CategoryView.
Thank you for your help Andriy, but it appears the problem was as simple as my monitor being far too insensitive. After I removed event hooks for all content on manipulation events, leaving only the hooks in the parent Grid, and then pressed VERY hard with two fingers on the screen, I got the events to fire. Probably need to mess a bit more with the routing, though.

Selecting a ListBoxItem when its inner ComboBox is focused

I have a DataTemplate that will be a templated ListBoxItem, this DataTemplate has a
ComboBox in it which when it has focus I want the ListBoxItem that this template
represents to become selected, this looks right to me. but sadly enough it doesn't work =(
So the real question here is within a DataTemplate is it possible to get or set the value
of the ListBoxItem.IsSelected property via a DataTemplate.Trigger?
<DataTemplate x:Key="myDataTemplate"
DataType="{x:Type local:myTemplateItem}">
<Grid x:Name="_LayoutRoot">
<ComboBox x:Name="testComboBox" />
</Grid>
<DataTemplate.Triggers>
<Trigger Property="IsFocused" value="true" SourceName="testComboBox">
<Setter Property="ListBoxItem.IsSelected" Value="true" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
<ListBox ItemTemplate="{StaticResource myDataTemplate}" />
I found a solution for your problem.
The problem is that when you have a control on your listboxitem, and the control is clicked (like for inputting text or changing the value of a combobox), the ListBoxItem does not get selected.
this should do the job:
public class FocusableListBox : ListBox
{
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is FocusableListBoxItem);
}
protected override System.Windows.DependencyObject GetContainerForItemOverride()
{
return new FocusableListBoxItem();
}
}
--> Use this FocusableListBox in stead of the default ListBox of WPF.
And use this ListBoxItem:
public class FocusableListBoxItem : ListBoxItem
{
public FocusableListBoxItem()
{
GotFocus += new RoutedEventHandler(FocusableListBoxItem_GotFocus);
}
void FocusableListBoxItem_GotFocus(object sender, RoutedEventArgs e)
{
object obj = ParentListBox.ItemContainerGenerator.ItemFromContainer(this);
ParentListBox.SelectedItem = obj;
}
private ListBox ParentListBox
{
get
{
return (ItemsControl.ItemsControlFromItemContainer(this) as ListBox);
}
}
}
A Treeview does also have this problem, but this solution does not work for a Treeview, 'cause SelectedItem of Treeview is readonly.
So if you can help me out with the Treeview please ;-)
I found that I preferred to use this:
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"></Setter>
</Trigger>
</Style.Triggers>
</Style>
Simple and works for all the listboxitems, regardless of what's inside.
No idea why your trigger don't work. To catch the get focus event of the combo box (or any control inside a listbox item) you can use attached routed events. You could put the code also in a derived listbox if you need this behavior in other parts of your application.
XAML:
<Window x:Class="RoutedEventDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Specialized="clr-namespace:System.Collections.Specialized;assembly=System"
xmlns:System="clr-namespace:System;assembly=mscorlib"
Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="myDataTemplate">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" Margin="5,0"/>
<ComboBox Width="50">
<ComboBoxItem>AAA</ComboBoxItem>
<ComboBoxItem>BBB</ComboBoxItem>
</ComboBox>
</StackPanel>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemTemplate="{StaticResource myDataTemplate}">
<ListBox.ItemsSource>
<Specialized:StringCollection>
<System:String>Item 1</System:String>
<System:String>Item 2</System:String>
<System:String>Item 3</System:String>
</Specialized:StringCollection>
</ListBox.ItemsSource>
</ListBox>
</Grid>
</Window>
Code behind hooking up to all got focus events.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace RoutedEventDemo
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
EventManager.RegisterClassHandler(typeof(UIElement),
GotFocusEvent,
new RoutedEventHandler(OnGotFocus));
}
private static void OnGotFocus(object sender, RoutedEventArgs e)
{
// Check if element that got focus is contained by a listboxitem and
// in that case selected the listboxitem.
DependencyObject parent = e.OriginalSource as DependencyObject;
while (parent != null)
{
ListBoxItem clickedOnItem = parent as ListBoxItem;
if (clickedOnItem != null)
{
clickedOnItem.IsSelected = true;
return;
}
parent = VisualTreeHelper.GetParent(parent);
}
}
}
}

Resources