Focusing text through focus manager in xaml - c#

I have a WPF window that focuses a textbox when the window starts up via the FocusManager, which works. e.g.
<Window
...
FocusManager.FocusedElement="{Binding ElementName=nameTextBox}">
Upon focus, I'd like to also highlight the text in the textbox. I'd like to do this all in the XAML if possible, as I have no code in code behind and would like to keep it that way.

I know you want to keep .cs out of your project but it'll be quite hard to achieve, you can create a one off class with an attached property inside that can be used over and over in your xaml files.
public class AutoFocusBehavior : DependencyObject
{
public static bool IsAutoFocus(DependencyObject obj)
{
return (bool)obj.GetValue(IsAutoFocusProperty);
}
public static void SetIsAutoFocus(DependencyObject obj, bool value)
{
obj.SetValue(IsAutoFocusProperty, value);
}
public static readonly DependencyProperty IsAutoFocusProperty =
DependencyProperty.RegisterAttached("IsAutoFocus", typeof(bool), typeof(AutoFocusBehavior),
new PropertyMetadata(false, new PropertyChangedCallback((d, de) =>
{
if ((bool)de.NewValue)
{
FrameworkElement frameworkElement = (FrameworkElement)d;
frameworkElement.Unloaded += frameworkElement_Unloaded;
frameworkElement.IsVisibleChanged += new DependencyPropertyChangedEventHandler(frameworkElement_IsVisibleChanged);
frameworkElement.IsEnabledChanged += frameworkElement_IsEnabledChanged;
}
else
{
FrameworkElement frameworkElement = (FrameworkElement)d;
frameworkElement.Unloaded -= frameworkElement_Unloaded;
frameworkElement.IsVisibleChanged -= new DependencyPropertyChangedEventHandler(frameworkElement_IsVisibleChanged);
frameworkElement.IsEnabledChanged -= frameworkElement_IsEnabledChanged;
}
})));
static void frameworkElement_Unloaded(object sender, RoutedEventArgs e)
{
FrameworkElement frameworkElement = (FrameworkElement)sender;
frameworkElement.IsVisibleChanged -= new DependencyPropertyChangedEventHandler(frameworkElement_IsVisibleChanged);
frameworkElement.IsEnabledChanged -= frameworkElement_IsEnabledChanged;
}
static void frameworkElement_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (((bool)e.NewValue))
{
if (sender is System.Windows.Controls.TextBox)
{
System.Windows.Controls.TextBox textBox = sender as System.Windows.Controls.TextBox;
if (textBox != null)
{
textBox.SelectAll();
}
}
((FrameworkElement)sender).Focus();
}
}
static void frameworkElement_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (((bool)e.NewValue))
{
if (sender is System.Windows.Controls.TextBox)
{
System.Windows.Controls.TextBox textBox = sender as System.Windows.Controls.TextBox;
if (textBox != null)
{
textBox.SelectAll();
textBox.Focus();
}
}
((FrameworkElement)sender).Focus();
}
}
}
Then used in any xaml like this.
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Text="testing" local:AutoFocusBehavior.IsAutoFocus="True" Margin="133,87,292,198"/>
</Grid>
</Window>

Related

How to put a user control in a static layer ontop of all other controls?

I'm developing an autocomplete user control for WPF using XAML and C#.
I would like to have the pop-up for the suggestions to appear above all controls. Currently my pop up is a ListView . That causes problems since whenever I decide to show it the UI must find a place for it and to do so moves all the controls which are below it further down.
How can I avoid this? I assume I must put it in a layer which is above all of the other controls?
I have written "auto-complete" style controls before by using the WPF Popup control, combined with a textbox. If you use Popup it should appear, as you say, in a layer over the top of everything else. Just use Placement of Bottom to align it to the bottom of the textbox.
Here is an example that I wrote a while ago. Basically it is a text box which, as you type pops up a suggestions popup, and as you type more it refines the options down. You could fairly easily change it to support multi-word auto-complete style code editing situations if you wanted that:
XAML:
<Grid>
<TextBox x:Name="textBox"
Text="{Binding Text, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:IntelliSenseUserControl}}}"
KeyUp="textBox_KeyUp"/>
<Popup x:Name="popup"
Placement="Bottom"
PlacementTarget="{Binding ElementName=textBox}"
IsOpen="False"
Width="200"
Height="300">
<ListView x:Name="listView"
ItemsSource="{Binding FilteredItemsSource, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:IntelliSenseUserControl}}}"
SelectionChanged="ListView_Selected"/>
</Popup>
</Grid>
Code-behind:
public partial class IntelliSenseUserControl : UserControl, INotifyPropertyChanged
{
public IntelliSenseUserControl()
{
InitializeComponent();
DependencyPropertyDescriptor prop = DependencyPropertyDescriptor.FromProperty(ItemsSourceProperty, typeof(IntelliSenseUserControl));
prop.AddValueChanged(this, ItemsSourceChanged);
}
private void ItemsSourceChanged(object sender, EventArgs e)
{
FilteredItemsSource = new ListCollectionView((IList)ItemsSource);
FilteredItemsSource.Filter = (arg) => { return arg == null || string.IsNullOrEmpty(textBox.Text) || arg.ToString().Contains(textBox.Text.Trim()); };
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(IntelliSenseUserControl), new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });
public object ItemsSource
{
get { return (object)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(IntelliSenseUserControl), new PropertyMetadata(null));
#region Notified Property - FilteredItemsSource (ListCollectionView)
public ListCollectionView FilteredItemsSource
{
get { return filteredItemsSource; }
set { filteredItemsSource = value; RaisePropertyChanged("FilteredItemsSource"); }
}
private ListCollectionView filteredItemsSource;
#endregion
private void textBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return || e.Key == Key.Enter)
{
popup.IsOpen = false;
}
else
{
popup.IsOpen = true;
FilteredItemsSource.Refresh();
}
}
private void UserControl_LostFocus(object sender, RoutedEventArgs e)
{
popup.IsOpen = false;
}
private void ListView_Selected(object sender, RoutedEventArgs e)
{
if (listView.SelectedItem != null)
{
Text = listView.SelectedItem.ToString().Trim();
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
If your Window's content container is a Grid, you can simply do something like
<ListBox Grid.RowSpawn="99" Grid.ColumnSpan="99"/>
to "simulate" an absolute position. You then just have to set its position with Margin, HorizontalAlignment and VerticalAlignment so it lays around the desired control.

How to track down all Show or ShowDialog in App WPF

I has a requirement that to track down all Window.Show or ShowDialog() in WPF.
The main purpose is I want to know when all Window in App open or close.
Something like, when closing WindowA or ChildWindowA, I want to write AuditLog for which view was opened/closed, I don't want to write code for each Window or ChildWindow and write it in App instance level to handle all open/close Window or ChildWindow in App.
You could create a base class deriving from Window that handles the logging.
public class AuditLoggableWindow : Window
{
public AuditLoggableWindow()
{
Closing += OnClosing;
ContentRendered += OnShown;
}
protected void OnClosing(object o, CancelEventArgs e)
{
// log that window is closing now
}
protected void OnShown(object o, EventArgs e)
{
// log that the window has been shown
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : AuditLoggableWindow
{
public MainWindow()
{
InitializeComponent();
}
}
And in your XAML markup you need to Replace the Window tag with namespace:AuditLoggableWindow. As the namespace of my project is wpfApplication1, the markup would be as follows:
<wpfApplication1:AuditLoggableWindow x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</wpfApplication1:AuditLoggableWindow>
I would like to register an attached property:
public static class WindowLog
{
public static readonly DependencyProperty EnableLogProperty =
DependencyProperty.RegisterAttached(
"EnableLog",
typeof(bool),
typeof(WindowLog),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, OnEnableWindowLogChanged));
public static void SetEnableWindowLog(Window window, bool value)
{
window.SetValue(EnableLogProperty, value);
}
public static bool GetEnableWindowLog(Window element)
{
return (bool)element.GetValue(EnableLogProperty);
}
private static void OnEnableWindowLogChanged(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
Window window = dependencyObject as Window;
if (window == null)
{
return;
}
if (GetEnableWindowLog(window))
{
Register(window);
}
else
{
Unregister(window);
}
}
private static void Unregister(Window window)
{
window.Closing -= Window_Closing;
window.Activated -= Window_Activated;
window.Closed -= Window_Closed;
}
private static void Register(Window window)
{
window.Closing += Window_Closing;
window.Activated += Window_Activated;
window.Closed += Window_Closed;
}
private static void Window_Closed(object sender, EventArgs e)
{
Window window = (Window)sender;
window.Closing -= Window_Closing;
window.Activated -= Window_Activated;
window.Closed -= Window_Closed;
}
private static void Window_Activated(object sender, EventArgs e)
{
// do something
}
private static void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// do something
}
}
Using
<Window x:Class="Wpf.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:attachments="clr-namespace:Wpf.Attachments"
attachments:WindowLog.EnableWindowLog="true">
<StackPanel>
</StackPanel>

Crash on user control custom event

I've made a simple custom control with ClickEvent:
ImageButton.xaml:
<UserControl x:Name="ImgButton" x:Class="WpfApplication1.ImageButton"
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>
ImageButton.xaml.cs:
public partial class ImageButton : UserControl
{
private bool mouse_down = false;
private bool mouse_in = false;
public event EventHandler Click;
public ImageButton()
{
this.MouseEnter += new MouseEventHandler(ImageButton_MouseEnter);
this.MouseLeave += new MouseEventHandler(ImageButton_MouseLeave);
this.MouseLeftButtonDown += new MouseButtonEventHandler(ImageButton_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(ImageButton_MouseLeftButtonUp);
InitializeComponent();
}
void ImageButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if ((mouse_down)&&(mouse_in))
{
Click(this, null);
}
mouse_down = false;
}
void ImageButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
mouse_down = true;
}
void ImageButton_MouseLeave(object sender, MouseEventArgs e)
{
mouse_in = false;
}
void ImageButton_MouseEnter(object sender, MouseEventArgs e)
{
mouse_in = true;
}
}
It works correct when I click on the control if I'm handling Click event, otherwise I get crash. So, what should I do?
You must check if handler was added to event beforre rising it:
void ImageButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if ((mouse_down) && (mouse_in) && Click != null)
{
Click(this, null);
}
mouse_down = false;
}

Dependency Property in User Control works only on the first instance

I have several custom user controls in a window. They appear dynamically, like workspaces.
I need to add a dependency property on an itemscontrol to trigger a scrolldown when an item is being added to the bound observable collection to my itemscontrol, like so:
(usercontrol)
<ScrollViewer VerticalScrollBarVisibility="Auto" >
<ItemsControl Grid.Row="0" ItemsSource="{Binding Messages}" View:ItemsControlBehavior.ScrollOnNewItem="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox IsReadOnly="True" TextWrapping="Wrap" Text="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
And the code of my dependency property :
public class ItemsControlBehavior
{
static readonly Dictionary<ItemsControl, Capture> Associations =
new Dictionary<ItemsControl, Capture>();
public static bool GetScrollOnNewItem(DependencyObject obj)
{
return (bool)obj.GetValue(ScrollOnNewItemProperty);
}
public static void SetScrollOnNewItem(DependencyObject obj, bool value)
{
obj.SetValue(ScrollOnNewItemProperty, value);
}
public static readonly DependencyProperty ScrollOnNewItemProperty =
DependencyProperty.RegisterAttached(
"ScrollOnNewItem",
typeof(bool),
typeof(ItemsControl),
new UIPropertyMetadata(false, OnScrollOnNewItemChanged));
public static void OnScrollOnNewItemChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var mycontrol = d as ItemsControl;
if (mycontrol == null) return;
bool newValue = (bool)e.NewValue;
if (newValue)
{
mycontrol.Loaded += new RoutedEventHandler(MyControl_Loaded);
mycontrol.Unloaded += new RoutedEventHandler(MyControl_Unloaded);
}
else
{
mycontrol.Loaded -= MyControl_Loaded;
mycontrol.Unloaded -= MyControl_Unloaded;
if (Associations.ContainsKey(mycontrol))
Associations[mycontrol].Dispose();
}
}
static void MyControl_Unloaded(object sender, RoutedEventArgs e)
{
var mycontrol = (ItemsControl)sender;
Associations[mycontrol].Dispose();
mycontrol.Unloaded -= MyControl_Unloaded;
}
static void MyControl_Loaded(object sender, RoutedEventArgs e)
{
var mycontrol = (ItemsControl)sender;
var incc = mycontrol.Items as INotifyCollectionChanged;
if (incc == null) return;
mycontrol.Loaded -= MyControl_Loaded;
Associations[mycontrol] = new Capture(mycontrol);
}
class Capture : IDisposable
{
public ItemsControl mycontrol{ get; set; }
public INotifyCollectionChanged incc { get; set; }
public Capture(ItemsControl mycontrol)
{
this.mycontrol = mycontrol;
incc = mycontrol.ItemsSource as INotifyCollectionChanged;
incc.CollectionChanged +=incc_CollectionChanged;
}
void incc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
ScrollViewer sv = mycontrol.Parent as ScrollViewer;
sv.ScrollToBottom();
}
}
public void Dispose()
{
incc.CollectionChanged -= incc_CollectionChanged;
}
}
}
During the first instantiation of my user control, it works like a charm.
But when another user control of the same type is dynamically instantiated, the DependencyProperty is never attached anymore to my scrollviewer. Only the first instance will work correctly.
I know that dependency properties are static, but does that mean they can't work at the same time on several user control of the same type added to the window?
Update 02/03 : Here's how I set the viewmodel to the view (not programmatically) :
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:testDp.ViewModel"
xmlns:View="clr-namespace:testDp.View">
<DataTemplate DataType="{x:Type vm:ChatTabViewModel}">
<View:ChatTabView />
</DataTemplate>
</ResourceDictionary>
even with x:shared = false in the datatemplate tag, it won't work.
But if I set the datacontext in a classic way like usercontrol.datacontext = new viewmodel(), it definitely work. But it's recommended to have a "shared" view, so how do we make dependency properties work with this "xaml" way of setting datacontext ?
Sorry, I couldn't reproduce your problem.
I started Visual C# 2010 Express, created a new 'WPF Application', added your XAML to a UserControl that I imaginatively titled UserControl1, and added your ItemsControlBehavior class. I then modified the MainWindow that VC# created for me as follows:
MainWindow.xaml (contents of <Window> element only):
<StackPanel Orientation="Vertical">
<Button Content="Add user control" Click="ButtonAddUserControl_Click" />
<Button Content="Add message" Click="ButtonAddMessage_Click" />
<StackPanel Orientation="Horizontal" x:Name="sp" Height="300" />
</StackPanel>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public ObservableCollection<string> Messages { get; private set; }
public MainWindow()
{
InitializeComponent();
Messages = new ObservableCollection<string>() { "1", "2", "3", "4" };
DataContext = this;
}
private void ButtonAddUserControl_Click(object sender, RoutedEventArgs e)
{
sp.Children.Add(new UserControl1());
}
private void ButtonAddMessage_Click(object sender, RoutedEventArgs e)
{
Messages.Add((Messages.Count + 1).ToString());
}
}
I made no modifications to the XAML in your UserControl, nor to your ItemsControlBehavior class.
I found that no matter how many user controls were added, their ScrollViewers all scrolled down to the bottom when I clicked the 'Add message' button.
If you're only seeing the scroll-to-the-bottom behaviour on one of your user controls, then there must be something that you're not telling us.

WPF: Can't get my control to take focus

I can't seem to get my control to actually take the focus:
XAML:
<Button Command={Binding SetGridToVisibleCommand} />
<Grid Visibility="{Binding IsGridVisible, Converter={con:VisibilityBooleanConverter}}">
<TextBox Text={Binding MyText} IsVisibleChanged="TextBox_IsVisibleChanged" />
</Grid>
XAML.cs:
private void TextBox_IsVisibleChanged(Object sender, DependencyPropertyChangedEventArgs e)
{
UIElement element = sender as UIElement;
if (element != null)
{
Boolean success = element.Focus(); //Always returns false and doesn't take focus.
}
}
The ViewModel does it's job of setting the IsGridVisible to true, and the converter does it's job by converting that value to Visibility.Visible (I snooped it).
Not all UIElements can be focused by default, have you tried setting Focusable to true before trying Focus()?
We use this with in our application:
public static class Initial
{
public static void SetFocus(DependencyObject obj, bool value)
{
obj.SetValue(FocusProperty, value);
}
public static readonly DependencyProperty FocusProperty =
DependencyProperty.RegisterAttached(
"Focus", typeof(bool), typeof(Initial),
new UIPropertyMetadata(false, HandleFocusPropertyChanged));
private static void HandleFocusPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = (UIElement)d;
if ((bool)e.NewValue)
element.Focus(); // Ignore false values.
}
}
And the usage is:
<TextBox Text="{Binding FooProperty, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1" HorizontalAlignment="Stretch"
Style="{StaticResource EditText}" ui:Initial.Focus="True"/>
The original idea came from SO, but couldn't find the answer.
100% free code behind :P
HTH
I couldn't reproduce your problem, its working fine with me, try the following code on a new project as a proof of concept and see if it works with you:
<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">
<StackPanel>
<Button Content="Click" Click="Button_Click" />
<Grid>
<TextBox Name="NameTextBox" Text="ddd"
IsVisibleChanged="TextBox_IsVisibleChanged" />
</Grid>
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TextBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
UIElement element = sender as UIElement;
if (element != null)
{
Boolean success = element.Focus(); //Always returns false and doesn't take focus.
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (NameTextBox.IsVisible)
NameTextBox.Visibility = Visibility.Collapsed;
else
NameTextBox.Visibility = Visibility.Visible;
}
}

Categories