I have been through several other postings about WPF and focus, and the best I can figure there's something I'm missing in my code. I'm working on an app using strict MVVM so I'm trying to avoid any code-behind in the view files (using attached behaviors when code-behind is necessary), but at this point even putting the focus code in the code-behind of the view it's not working.
I have an application with a main window and I'm trying to make a search window popup on a hot key. I'd like as soon as the user hits the hot key, the keyboard focus to be on the search text so it's just hotkey and then type your search term. Everything but the logical focus is working at this point, even though keyboard claims to have focus on the element.
I can't seem to get it to take both keyboard and logical focus at the same time from the code. However, if I hit Tab as soon as the search box appears, I'm put right into the text box.
Main window code:
<ribbon:RibbonWindow x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ribbon="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
xmlns:attached="clr-namespace:UserInterface.Attached"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF45"
xmlns:viewModels="clr-namespace:UserInterface.ViewModels"
xmlns:views="clr-namespace:UserInterface.Views"
xmlns:layout="clr-namespace:UserInterface.ViewModels.Layout"
xmlns:layout1="clr-namespace:UserInterface.Views.Layout"
MinHeight="560"
MinWidth="950"
WindowStartupLocation="CenterScreen"
Icon="{Binding Layout.IconPath}"
DataContext="{Binding Main, Source={StaticResource Locator}}"
FocusManager.FocusedElement="{Binding ElementName=LayoutControl}"
Title="{Binding Layout.Title}">
<!-- Ribbon menu shortcuts -->
<Window.InputBindings>
<KeyBinding Modifiers="Control" Key="T" Command="{Binding Layout.Commands[GlobalObjectSearch]}" />
</Window.InputBindings>
<Grid>
<ContentPresenter Content="{Binding Layout}" x:Name="LayoutControl">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type layout:MainViewModel}">
<layout1:MainView/>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</ribbon:RibbonWindow>
Code to make search window appear:
public SelfClosingDialogView ShowSelfClosingDialog(IWindowDialogViewModel dataContext)
{
dataContext.CheckWhetherArgumentIsNull(nameof(dataContext));
var view = new SelfClosingDialogView
{
DataContext = dataContext,
Owner = Application.Current?.MainWindow
};
view.Show();
return view;
}
Search window code (Reused, so generic):
<Window x:Class="UserInterface.Views.DialogViews.SelfClosingDialogView"
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:attached="clr-namespace:UserInterface.Attached"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
SizeToContent="WidthAndHeight"
WindowStyle="None"
WindowStartupLocation="CenterOwner">
<!-- Allow view models to cause the window to close -->
<Window.Style>
<Style TargetType="{x:Type Window}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsClosed}" Value="true">
<!-- Executes close -->
<Setter Property="attached:WindowCloseBehavior.Close" Value="true" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Style>
<!-- Displays the passed-in view model -->
<Grid>
<ContentPresenter x:Name="DialogPresenter" Content="{Binding}" Margin="0" />
</Grid>
</Window>
Code for my search view:
<UserControl x:Class="UserInterface.Views.DialogViews.ObjectSearchView"
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:dialogViewModels="clr-namespace:UserInterface.ViewModels.DialogViewModels"
xmlns:utils="clr-namespace:WPF.Utils"
xmlns:attached="clr-namespace:UserInterface.Attached"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance dialogViewModels:ObjectSearchViewModel}"
MinWidth="250"
Focusable="True"
FocusManager.IsFocusScope="True">
<UserControl.InputBindings>
<KeyBinding Key="Enter" Command="{Binding BrowseToObjectCommand}" />
<KeyBinding Key="Escape" Command="{Binding CloseWindowCommand}" />
</UserControl.InputBindings>
<UserControl.Resources>
<Style BasedOn="{StaticResource FormTextBlockStyle}" TargetType="TextBlock" />
</UserControl.Resources>
<StackPanel>
<TextBox Name="SearchText"
Focusable="True"
Text="{utils:ValidatingLiveBinding SearchText}"
attached:NavigatingListBoxBehavior.LinkedListBox="{Binding ElementName=SearchResults}">
</TextBox>
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Auto"
MaxHeight="400">
<ListBox Name="SearchResults"
ItemsSource="{Binding SearchResults}"
SelectedItem="{Binding SelectedSearchItem}"
Visibility="{Binding HasSearchResults, Converter={StaticResource BooleanToVisibilityConverter}}"
attached:ItemsControlProperties.DoubleClickCommand="{Binding BrowseToObjectCommand}"
KeyboardNavigation.IsTabStop="False"
IsSynchronizedWithCurrentItem="True" />
</ScrollViewer>
</StackPanel>
</UserControl>
And finally, the code-behind hack I'm trying to attempt to get focus (plus debugging code so that I don't lose the focus switching back and forth to Visual Studio):
public partial class ObjectSearchView : UserControl
{
public ObjectSearchView()
{
InitializeComponent();
this.Loaded += this.OnLoad;
}
private void OnLoad(object sender, RoutedEventArgs e)
{
this.PrintFocusInfo();
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(this), this.SearchText);
this.PrintFocusInfo();
this.SearchText.Focus();
this.PrintFocusInfo();
Keyboard.Focus(this.SearchText);
this.PrintFocusInfo();
}
[Conditional("DEBUG")]
private void PrintFocusInfo()
{
var logicalElement = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this.SearchText));
Debug.WriteLine("Current logical focus is on '{0}', of type '{1}' ({2})".FormatInvariantCulture((logicalElement as FrameworkElement)?.Name, logicalElement?.GetType().Name, logicalElement));
var focusedElement = Keyboard.FocusedElement;
Debug.WriteLine(
"Current Keyboard Focus is on '{0}', of type '{1}' ({2})".FormatInvariantCulture(
(focusedElement as FrameworkElement)?.Name,
focusedElement.GetType().Name,
focusedElement));
}
}
Output window contents:
Current logical focus is on '', of type '' ()
Current Keyboard Focus is on '', of type 'MainWindow' (UserInterface.Views.MainWindow)
Current logical focus is on '', of type '' ()
Current Keyboard Focus is on '', of type 'MainWindow' (UserInterface.Views.MainWindow)
Current logical focus is on '', of type '' ()
Current Keyboard Focus is on 'SearchText', of type 'TextBox' (System.Windows.Controls.TextBox)
Current logical focus is on '', of type '' ()
Current Keyboard Focus is on 'SearchText', of type 'TextBox' (System.Windows.Controls.TextBox)
I've tried to include everything I can think of, but I cannot get logical focus to show anything except null.
Here is the behavior I ultimately created which fixed this for me. There's still a lot I don't know about WHY this works... but if you're having a problem getting Focus to cooperate it looks like the key is catching it when IsVisible is set to true, and having the Dispatcher set the focus for you. I linked this event to the IsVisibleChanged element on the textbox (through an attached behavior).
private void SetFocusOnVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
this.Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() => this.AssociatedObject.Focus()));
}
}
Related
I have a window with a ContentControl binding:
<ContentControl Content="{Binding CurrentViewModel}" />
I also have an empty user control binds to the ContentControl:
<UserControl x:Class="UserControl1"
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="300" d:DesignWidth="300">
<Grid>
</Grid>
</UserControl>
When I run and press the tab keyboard I get a dotted rectangle around the content control. How can I disable this?
I tried using Focusable="False" and FocusVisualStyle="{x:Null}" without success...
Have you tried setting IsTabStop="False", for example...
<ContentControl Height="200" Width="200" IsTabStop="False">
<ContentControl.Content>
<TextBox/>
</ContentControl.Content>
</ContentControl>
and I would suggest you combine it with this trick:
Loaded += (sender, e) =>
MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
from the answer to this question: WPF and initial focus
Override the ContentControl Style and add property FocusVisualStyle as null and implement by using style property
<ContentControl Height="200" Width="200" FocusVisualStyle="{x:Null}">
<ContentControl.Content>
<local:UserControl1/>
</ContentControl.Content>
</ContentControl>
So the problem is this. I need UserControl which will have set Canvas.Top and Canvas.Left but these properties are binded from the ViewModel. For simplicity let's have this code for the user control with no code behind:
<UserControl x:Class="BadBinding.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Canvas.Left="{Binding ElementName=slider, Path=Value}"
>
<Grid Width="100" Background="Red">
<Slider x:Name="slider" Minimum="100" Maximum="250" />
</Grid>
</UserControl>
And this code for the main window:
<Window x:Class="BadBinding.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"
xmlns:local="clr-namespace:BadBinding"
>
<Canvas>
<local:MyUserControl />
</Canvas>
</Window>
I don't know why is binding not working. When you set Canvas.Left directly to some value everything is fine as well as writing content of the user control directly to the main window.
I think its because the UserControl is constructed befor being added to the Canvas and since Canvas.Left is an attached property it probably won't resolve correctly.
Try using a Reference binding.
<UserControl x:Class="BadBinding.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Canvas.Left="{Binding Source={x:Reference Name=slider}, Path=Value}"
>
<Grid Width="100" Background="Red">
<Slider x:Name="slider" Minimum="100" Maximum="250" />
</Grid>
</UserControl>
Note: you may get a compile warning, but it will still compile.
But I think the best option would be to create a property on your usercontrol to bind the value, this will also work.
I tried a lot with Bindings but it dint worked for me too.. so if you wanna go with EventHandler then the following workaround may help you..
Remove the Bindings and add an event handler to ValueChanged event
In your MyUserControl.xaml
<UserControl x:Class="BadBinding.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Width="100" Background="Red">
<Slider x:Name="slider" Minimum="100" Maximum="250" ValueChanged="slider1_ValueChanged" />
</Grid>
</UserControl>
In your MyUserControl.xaml.cs
private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Canvas.SetLeft(this, slider1.Value);
}
I tried this and working for me if you find any problem then let me know..
I seem to have run into some behavior regarding WPF ResourceDictionaries, Brushes, and Styles (at least that's what I've noticed so far) that is counter to my understanding of how these things should work. Basically, if I reference a Brush from a Setter withing a Style in a ResourceDictionary it causes the Brush to become frozen. The example below illustrates this, as I get an InvalidOperationException when I try to change the Color on the shared Brush within my button's Click event handler. It should cause both Rectangle's color to change, as they both use the same shared Brush, but I get the exception instead.
<Window x:Class="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">
<Window.Resources>
<SolidColorBrush x:Key="TestBrush" Color="Red" />
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="{StaticResource TestBrush}" />
</Style>
</Window.Resources>
<StackPanel>
<Button Name="Button1" Content="Change Color" Click="Button1_Click" />
<Rectangle Height="20" />
<Rectangle Height="20" />
</StackPanel>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
var brush = (SolidColorBrush)FindResource("TestBrush");
// InvalidOperationException Here. Brush is Frozen/Read-Only
brush.Color = Colors.Blue;
}
}
If I simply remove the Style (more specifically the Setter) and reference the Brush (still from the ResourceDictionary) directly from each Rectangle, I get the expected behavior of the Rectangles' colors changing in tandem from the button click event. See code below (button click event hanlder remains the same).
<Window x:Class="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">
<Window.Resources>
<SolidColorBrush x:Key="TestBrush" Color="Red" />
</Window.Resources>
<StackPanel>
<Button Name="Button1" Content="Change Color" Click="Button1_Click" />
<Rectangle Height="20" Fill="{StaticResource TestBrush}" />
<Rectangle Height="20" Fill="{StaticResource TestBrush}" />
</StackPanel>
</Window>
I only see the Brush becoming frozen when it is referenced as a StaticResource from a Style's Setter. I can actaully reference the same Brush from other locations within the ResourceDictionary without it becoming frozen; i.e. the contents of ControlTemplates.
Can anyone please explain what is going on with this strange behavior and if it's by-design or a bug?
Thanks,
Brandon
...once a style has been applied, it is sealed and cannot be changed.
If you want to dynamically change a style that has already been
applied, you must create a new style to replace the existing one. For
more information, see the IsSealed property.
See http://msdn.microsoft.com/en-us/library/ms745683.aspx
I’m having issues with a WPF Expander that I have in a user control that gets rendered in a ListBox. Essentially I’m trying to get PopUpButton behavior on each ListItem in my ListBox. When I open the Expander the content is rendering behind everything else as if it were transparent, or lower in the z-order. I’ve tried this with a WPF PopUp and Toggle Button as well (using techniques described int Karle Shivllet’s blog – Expander Control with Popup Content) to no avail.
Let me first describe what it is I’m trying to do. I have two controls that display a list of inputs that I need to configure for my application. For simplicity sake, one user control is used to configure inputs to a graph, and another control is used to control inputs to a simple excel grid. The inputs for the graph and grid each have properties that need to be configured on them. I’ve developed a simple user control called InputSelectControl that will render a ListBox containing the list of inputs to be configured for the graph or grid. Each ListItem in the ListBox consist of a TextBlock for the input’s name (e.g. Pressure, ECG, etc.) and a WPF Expander that , when clicke, displays a property editor for that input. Since the property editor presentation will be different depending on whether I’m dealing with graph inputs versus grid inputs, I’ve used a DependencyProperty on my InputSelectControl that is of type ControlTemplate. This allows my grid and graph to each supply the presentation they need for editing their input properties. Also note that I will have more than just a graph and a grid that need this behavior, thus the desire to make this a user control that can dynamically receive presentation behavior.
I’ve tried placing my Expander inside my property editor template, had have also tried experimenting with the ZIndex in various places, but always end up with the same behavior, the Expander popup displays behind the ListItems in my list.
Below is some code further describing my approach. Hopefully someone can help me out of this pickle.
XAML representing my Grid (could be graph, or something else) control that hold my InputSelectControl:
<UserControl x:Class="MyApp.GridView"
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:props="clr-namespace:PopupButtonDependencyProp" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<!-- Specify the control tempalte we want loaded into the
properies popup for a grid-->
<ControlTemplate x:Key="GridPropertyEditorTemplate" TargetType="ContentControl">
<props:GridInputPropertyEditor />
</ControlTemplate>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Hello Grid" Margin="5" />
<!-- Tell the InputSelectControl what template to load into Property
Window for each Grid Input item -->
<props:InputSelectControl Grid.Row="1"
DataContext="{Binding VmUsedInputs, Mode=OneWay}"
PropertyEditorTemplate="{StaticResource GridPropertyEditorTemplate}" />
</Grid>
</UserControl>
XAML representing my InputSelectControl that displays my list of inputs and a ContentControl place holder for each ListItem where I want my "Popup behavior" for editing properties:
<UserControl x:Class="MyApp.InputSelectControl"
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:props="clr-namespace:PopupButtonDependencyProp"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<!-- Listbox holding our inputs. Assuming whatever we're contained in has
set our DataContext to a valid Input collection-->
<ListBox x:Name="inputsUsed" Grid.Row="1" ItemsSource="{Binding}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionMode="Multiple" ClipToBounds="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Border x:Name="border" CornerRadius="7">
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal">
<!-- Input label-->
<TextBlock Text="{Binding Path=Label}" FontWeight ="Bold"
FontSize ="12" FontStyle = "Normal"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="5,0,5,0" />
<Expander x:Name="GridPropEditor" Header="Properties"
Height="Auto" Margin="5,0,0,0"
ToolTip="Open trace property dialog">
<!-- Properties button - The ContentControl below is rendering
the PropertyEditorTemplate that was set by whoever contains us -->
<ContentControl Template="{Binding PropertyEditorTemplate,
RelativeSource={RelativeSource AncestorType=props:InputSelectControl}}" />
</Expander>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
C# representing my DependencyProperty for injection the property editor template to present on popup.
/// <summary>
/// Interaction logic for InputSelectControl.xaml
/// </summary>
public partial class InputSelectControl : UserControl
{
#region Dependency Property stuff
/// <summary>
/// Dependency Property for control template to be rendered. This
/// lets us adorn the InputSelectControl with content in the Xaml.
/// The content can be different fore each instance of InputSelectControl.
/// </summary>
public static DependencyProperty PropertyEditorTemplateProperty =
DependencyProperty.Register("PropertyEditorTemplate",
typeof(ControlTemplate), typeof(InputSelectControl));
/// <summary>
/// PropertyEditorTemplate. This is how the property is set and get by WPF
/// </summary>
public ControlTemplate PropertyEditorTemplate
{
get { return GetValue(PropertyEditorTemplateProperty) as ControlTemplate; }
set { SetValue(PropertyEditorTemplateProperty, value); }
}
#endregion
/// <summary>
/// Constructor
/// </summary>
public InputSelectControl()
{
InitializeComponent();
}
}
XAML representing my GridInputPropertyEditor which is the template describing the presentation for editing Grid properties. This will be different for a Graph:
<UserControl x:Class="MyApp.GridInputPropertyEditor"
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="300" d:DesignWidth="300">
<Canvas Panel.ZIndex=”99”>
<!-- Property Editor Control - Assumes DataContext holds to the properties
that need to be edited-->
<StackPanel Orientation="Vertical" Background="WhiteSmoke">
<!-- Lists the properties for a Grid to be edited. We could use
any layout we need here. -->
<ListBox ItemsSource="{Binding Properties}" Background="WhiteSmoke" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5,5">
<TextBlock Text="{Binding Label}" FontWeight="Bold"/>
<TextBlock Text=":" />
<TextBox Text="{Binding Value}" Margin="10,0" Width="20" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Canvas>
</UserControl>
Using Snoop I was able to figure out that if I set the Z-Index of the ListBox item to a high number my property editor comes to the foreground. If someone sees a better way around this let me know. Otherwise I could use some help coming up with a trigger to raise and lower the zindex based on the item selected.
Okay, after a few trials and tribulations I was able to come up with solution using code behind. I'd be interested in finding a way to do this with triggers, but I'm not sure it's possible with this approach.
Here's the update expander XAML:
<Expander x:Name="GridPropEditor" Header="Properties" Height="Auto" Margin="5,0,0,0"
ToolTip="Open trace property dialog"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}}"
PreviewMouseDown="GridPropEditor_PreviewMouseDown"
Expanded="GridPropEditor_Expanded">
Here's the code behind I added to my xaml.cs file
//When an expander is expanded, collapse all the other expanders
private void GridPropEditor_Expanded(object sender, RoutedEventArgs e)
{
if (ExpandersSelected == null)
ExpandersSelected = new List<Expander>();
var expander = (sender as Expander);
var listbox = expander.Tag as ListBoxItem;
if (!ExpandersSelected.Contains(expander))
ExpandersSelected.Add(expander);
if (ExpandersSelected != null)
{
foreach(var x in ExpandersSelected)
{
if (x.Equals(expander))
{
listbox.SetValue(Panel.ZIndexProperty, 99);
continue;
}
var l = x.Tag as ListBoxItem;
x.IsExpanded = false;
l.SetValue(Panel.ZIndexProperty, 0);
}
}
}
The code behind solution closes any expanders that might already be opened and brings the currently expanding Expanders container to the foreground by setting the zindex to 99.
Again, if anyone has a better solution I'm all ears.
I'm a bit of a WPF noob, so apologies for any inherent daftness in this question (!)
I'm trying to enable editing of WPF treeview labels with a doubleclick - I've googled around this, and it looks like the two ways of doing this are with a custom control or with a style which hides one of a TextBox/TextBlock.
Using a style to set the label to be a textBox based on a DataTrigger seems easy enough (eg 1 below), but it means that any time a row is selected, it's 'being edited'.
What I'd really like to do is to enable this (Transition to textbox) on a mousedoubleclick event, but it seems that EventTriggers can't be used in the manner below, because they're transient. (It doesn't seem I can simply use the DoubleClick event in codebehind, because that doesn't (??) allow me to affect the displayed controls to show / hide the textboxes).
Using a full blown custom control seems like the alternative - there's an AAALMOST working example here ( http://www.codeproject.com/KB/WPF/editabletextblock.aspx ), however it doesn't work in the presence of HierachicalDataTemplate clauses (and it doesn't look like a solution is forthcoming).
(eg 1 - switch from textblock to textbox when selected)
<Window x:Class="treetest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:treetest"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Style x:Key="EditableContentControl" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type local:CompositeViewModel}">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected,RelativeSource=RelativeSource AncestorType={x:Type TreeViewItem}}}"
Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type local:CompositeViewModel}">
<TextBox Text="{Binding Path=Name,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<TreeView Margin="12,12,115,12" Name="treeView1"
ItemsSource="{Binding Path=GetRootData}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:CompositeViewModel}" ItemsSource="{Binding Path=Children}">
<ContentControl Content="{Binding}" Style="{StaticResource EditableContentControl}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
does it help:
string name = "some name";
var treeItem = new TreeViewItem()
{
Header = name,
};
var textBox = new TextBox()
{
Text = name,
};
treeItem.MouseDoubleClick += (o, e) =>
{
TreeItem.Header = textBox;
};
textBox.LostFocus += (o, e) =>
{
treeItem.Header = textBox.Text;
name = textBox.Text;
};
it quite simple and it works for me fine.
What if instead of triggering on IsSelected you trigger on a custom property of your bound data like IsEditing? You can then set IsEditing to true whenever you want to change to happen (i.e. in your case, when the mouse button is clicked).
Have a look at CallActionMethod from Blend. This special trigger lets you make a loose link between any event like double click and a method in your code behind.
If you prefer using a command then you can do the same with InvokeCommandAction. You can hook up any command to an event.