I'm using the lastest version of Silverlight 2.0 within Visual Studio 2008. I have a simple Silverlight UserControl with the following code:
public partial class SilverlightControl1 : UserControl
{
public SilverlightControl1()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(SilverlightControl1_Loaded);
Composite = new Composite();
}
void SilverlightControl1_Loaded(object sender, RoutedEventArgs e)
{
Composite.Width = this.Width / 2.0;
Composite.Height = this.Height / 2.0;
if (!this.LayoutRoot.Children.Contains(Composite))
this.LayoutRoot.Children.Add(Composite);
}
public Composite Composite
{
get;
set;
}
}
public class Composite : ContentControl
{
private Grid grid;
private Canvas canvas;
public Composite()
{
if (grid == null) grid = new Grid();
if (canvas == null) canvas = new Canvas();
if (!grid.Children.Contains(canvas))
grid.Children.Add(canvas);
Content = grid;
this.Loaded += new RoutedEventHandler(Composite_Loaded);
}
private Rectangle rectangle;
void Composite_Loaded(object sender, RoutedEventArgs e)
{
if (rectangle == null) rectangle = new Rectangle();
Canvas.SetTop(rectangle, 0);
Canvas.SetLeft(rectangle, 0);
rectangle.Fill = new SolidColorBrush(Color);
rectangle.Width = Width;
rectangle.Height = Height;
if (!canvas.Children.Contains(rectangle))
canvas.Children.Add(rectangle);
}
public Color Color
{
get;
set;
}
}
I then use this UserControl in a Silverlight application, the XAML of the page looking like this:
<UserControl x:Class="SilverlightApplication1.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:test="clr-namespace:SilverlightClassLibrary1;assembly=SilverlightClassLibrary1"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="Green">
<test:SilverlightControl1 Name="uControl1">
<test:SilverlightControl1.Composite>
<test:Composite Color="Yellow"/>
</test:SilverlightControl1.Composite>
</test:SilverlightControl1>
</Grid>
</UserControl>
My question is: what code do I have to add to the above so that by changing "Composite Color" to something other than Yellow and hitting the return button, the UserControl automatically refreshes? As the code is, the only way to refresh the UserControl is by moving the Slider bar within the VS2008 IDE which changes the percentage zoom of the Silverlight Page. A side question, although of lesser importance to the above question, is: with the code as it is above, why can't I change the "Background" color of the LayoutRoot? If I remove my UserControl it works as expected.
The solution was two-fold. Firstly to make changes in the LayoutUpdated event rather than the Loaded event and secondly to subscribe to the PropertyChangedCallback of the PropertyMetadata. Here's the complete working code:
public partial class SilverlightControl1 : UserControl
{
public SilverlightControl1()
{
InitializeComponent();
this.LayoutUpdated += new EventHandler(SilverlightControl1_LayoutUpdated);
Composite = new Composite();
}
void SilverlightControl1_LayoutUpdated(object sender, EventArgs e)
{
Composite.Width = this.Width / 2.0;
Composite.Height = this.Height / 2.0;
if (!this.LayoutRoot.Children.Contains(Composite)) this.LayoutRoot.Children.Add(Composite);
}
public Composite Composite
{
get;
set;
}
}
public class Composite : ContentControl
{
private Grid grid;
private Canvas canvas;
public Composite()
{
if (grid == null) grid = new Grid();
if (canvas == null) canvas = new Canvas();
if (!grid.Children.Contains(canvas)) grid.Children.Add(canvas);
Content = grid;
this.LayoutUpdated += new EventHandler(Composite_LayoutUpdated);
}
void Composite_LayoutUpdated(object sender, EventArgs e)
{
if (rectangle == null) rectangle = new Rectangle();
Canvas.SetTop(rectangle, 0);
Canvas.SetLeft(rectangle, 0);
rectangle.Fill = new SolidColorBrush(Color);
rectangle.Width = Width;
rectangle.Height = Height;
if (!canvas.Children.Contains(rectangle)) canvas.Children.Add(rectangle);
}
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(Composite), new PropertyMetadata(Colors.Red, new PropertyChangedCallback(OnColorPropertyChanged)));
private static void OnColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Composite comp = (Composite)d;
comp.InvalidateArrange();
}
private Rectangle rectangle;
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
}
I think you need to make Composite into a Dependency Proprety. Probably will want to do the same thing for Color on Composite so that you'll be able to bind it.
Related
Using MVVM architecture, I want to load an image on a button.
As I have similar button in many places I want to make this as common.
1) Initially when the form is launched an image should be loaded.
2) When "MouseEnter" event then cursor image should change to hand.
3) When "MouseLeave" event then cursor image should change to Arrow.
4) When "MouseLeftButtonDown" then an image should be loaded.
5) When "MouseLeftButtonUp" then an image should be loaded.
Like this, I want to load different images on different events and this button needs to be used in
many other dialogs. How can design it such that it can be as a common resource for all.
Could any one please help me to solve this issue.
I am very new to WPF. Please be kind with your answers.
You could use attached properties for this. If you need different events, then you will have to add more attached properties.
Note that #2 and #3 could simply be Cursor="Hand" on the button. There is nothing to implement for #3 - the cursor will change on it's own when you leave the button. I added it as an attached property just for fun. It is completely useless and really just duplicates Cursor="Hand".
disk_blue.png, disk_green.png and disk_yellow.png were added to the root of the application by just dragging and dropping them onto the project in Visual Studio. The build action is "Resource".
Here is a complete demo app:
XAML:
<Window x:Class="WpfApplication56.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:WpfApplication56"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:VM InitialImage="disk_blue.png" MouseLeftButtonDownImage="disk_green.png" MouseLeftButtonUpImage="disk_yellow.png" />
</Window.DataContext>
<Grid>
<Button VerticalAlignment="Center" HorizontalAlignment="Center" Padding="20"
local:MyButton.MouseEnterCursor="Hand"
local:MyButton.InitialImage="{Binding InitialImage}"
local:MyButton.MouseLeftButtonDown="{Binding MouseLeftButtonDownImage}"
local:MyButton.MouseLeftButtonUp="{Binding MouseLeftButtonUpImage}"
/>
</Grid>
</Window>
CS:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
namespace WpfApplication56
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class VM // I didn't implement INotifyPropertyChanged because it doesn't matter for this question
{
public Uri InitialImage { get; set; }
public Uri MouseLeftButtonDownImage { get; set; }
public Uri MouseLeftButtonUpImage { get; set; }
}
public class MyButton
{
// Attached Properties
#region MouseEnterCursorProperty
public static Cursor GetMouseEnterCursor(DependencyObject obj)
{
return (Cursor)obj.GetValue(MouseEnterCursorProperty);
}
public static void SetMouseEnterCursor(DependencyObject obj, Cursor value)
{
obj.SetValue(MouseEnterCursorProperty, value);
}
public static readonly DependencyProperty MouseEnterCursorProperty = DependencyProperty.RegisterAttached("MouseEnterCursor", typeof(Cursor), typeof(MyButton), new PropertyMetadata(null, OnPropertyChanged));
#endregion
#region InitialImageProperty
public static Uri GetInitialImage(DependencyObject obj)
{
return (Uri)obj.GetValue(InitialImageProperty);
}
public static void SetInitialImage(DependencyObject obj, Uri value)
{
obj.SetValue(InitialImageProperty, value);
}
public static readonly DependencyProperty InitialImageProperty = DependencyProperty.RegisterAttached("InitialImage", typeof(Uri), typeof(MyButton), new PropertyMetadata(null, OnPropertyChanged));
#endregion
#region MouseLeftButtonDownProperty
public static Uri GetMouseLeftButtonDown(DependencyObject obj)
{
return (Uri)obj.GetValue(MouseLeftButtonDownProperty);
}
public static void SetMouseLeftButtonDown(DependencyObject obj, Uri value)
{
obj.SetValue(MouseLeftButtonDownProperty, value);
}
public static readonly DependencyProperty MouseLeftButtonDownProperty = DependencyProperty.RegisterAttached("MouseLeftButtonDown", typeof(Uri), typeof(MyButton), new PropertyMetadata(null, OnPropertyChanged));
#endregion
#region MouseLeftButtonUpProperty
public static Uri GetMouseLeftButtonUp(DependencyObject obj)
{
return (Uri)obj.GetValue(MouseLeftButtonUpProperty);
}
public static void SetMouseLeftButtonUp(DependencyObject obj, Uri value)
{
obj.SetValue(MouseLeftButtonUpProperty, value);
}
public static readonly DependencyProperty MouseLeftButtonUpProperty = DependencyProperty.RegisterAttached("MouseLeftButtonUp", typeof(Uri), typeof(MyButton), new PropertyMetadata(null, OnPropertyChanged));
#endregion
// Called when the value for an attached property has changed
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Button b = d as Button;
if (b == null)
return;
if (e.Property.Equals(MouseEnterCursorProperty))
{
b.MouseEnter -= MouseEnter; // In case the property changes more than once we don't want to hook it more than once
b.MouseEnter += MouseEnter;
}
else if (e.Property.Equals(InitialImageProperty))
{
SetButtonImage(b, GetInitialImage(b));
}
else if (e.Property.Equals(MouseLeftButtonDownProperty))
{
// Had to use Preview because non-Preview never fired
b.PreviewMouseLeftButtonDown -= MouseLeftButtonDown;
b.PreviewMouseLeftButtonDown += MouseLeftButtonDown;
}
else if (e.Property.Equals(MouseLeftButtonUpProperty))
{
// Had to use Preview because non-Preview never fired
b.PreviewMouseLeftButtonUp -= MouseLeftButtonUp;
b.PreviewMouseLeftButtonUp += MouseLeftButtonUp;
}
}
private static void MouseEnter(object sender, MouseEventArgs e)
{
Button b = sender as Button;
if (b == null)
return;
b.Cursor = GetMouseEnterCursor(b);
e.Handled = false;
}
private static void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Button b = sender as Button;
if (b == null)
return;
SetButtonImage(b, GetMouseLeftButtonDown(b));
e.Handled = false;
}
private static void MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Button b = sender as Button;
if (b == null)
return;
SetButtonImage(b, GetMouseLeftButtonUp(b));
e.Handled = false;
}
private static void SetButtonImage(Button b, Uri uri)
{
if (b == null || uri == null)
return;
var img = b.Content as Image;
if (img == null)
img = new Image();
BitmapImage bm = new BitmapImage();
bm.BeginInit();
bm.UriSource = uri;
bm.EndInit();
img.Source = bm;
b.Content = img;
}
}
}
Screenshots
Initial (mouse was over it to show cursor change but cursor isn't in the image):
LeftMouseButtonDown:
LeftMouseButtonUp:
Im new in WPF and Im creating a custom dialog (or a message box) in WPF.
My problem is I cant change everything behind my custom dialog to Opacity=0.5. Only the form who called the custom dialog..
I just want to show my dialog box like in Windows 8.1
Anything would be highly appreciated.
TIA!
Set a property in your MainWindow that controls Opacity of the LayoutRoot. This will change the opacity of your App when the dialog is displayed.
Sample code:
<Grid Name="LayoutRoot" Opacity="{Binding MainWindowOpacity}">
<StackPanel>
<Button Click="Button_Click" Content="Click Me to Show Dialog"/>
<TextBlock Text="WPF" FontSize="72" Margin="50" Foreground="Orange" HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
and
public partial class MainWindow : Window
{
public double MainWindowOpacity
{
get { return (double)GetValue(MainWindowOpacityProperty); }
set { SetValue(MainWindowOpacityProperty, value); }
}
// Using a DependencyProperty as the backing store for MainWindowOpacity. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MainWindowOpacityProperty =
DependencyProperty.Register("MainWindowOpacity", typeof(double), typeof(MainWindow), new PropertyMetadata(1.0));
public MainWindow()
{
InitializeComponent();
DataContext = this;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//if (MainWindowOpacity < 1) MainWindowOpacity = 1.0;
//else MainWindowOpacity = 0.5;
MainWindowOpacity = 0.5;
// show dialog
// boilerplate code from http://stackoverflow.com/questions/6417558/modal-dialog-not-showing-on-top-of-other-windows
Window window = new Window()
{
Title = "WPF Modal Dialog",
ShowInTaskbar = false, // don't show the dialog on the taskbar
Topmost = true, // ensure we're Always On Top
ResizeMode = ResizeMode.NoResize, // remove excess caption bar buttons
Owner = Application.Current.MainWindow,
Width = 300,
Height = 200
};
window.ShowDialog();
MainWindowOpacity = 1.0;
}
}
and the result:
You could add a static method to App.xaml like this:
public partial class App : Application
{
public static void SetWindowsToOpacity(double dOpacity, Window ignoreWindow)
{
foreach (Window win in System.Windows.Application.Current.Windows)
{
if (win != ignoreWindow)
{
win.Opacity = dOpacity;
}
}
}
}
Pass in the Dialog window that you do not want to be set to lower opacity and an opacity value for all the rest.
Call the method like this:
private void button_Click(object sender, RoutedEventArgs e)
{
Window1 dialog = new Window1();
App.SetWindowsToOpacity(0.5, dialog);
dialog.ShowDialog();
App.SetWindowsToOpacity(1.0, null);
}
I want to Implement special button and i don't know even how to start with this.
I want my Button's content property to be: Play. When clicking on it, I want 2 other Buttons to pop up in the left and in the right sides: Single Play and Parallel Play
All you have to do is to create your 3 buttons and then put a visibility converter on your 2 sides buttons. Create a property that will hold if they should be visible or not and bind the visibility converter to this property. The Play button should modify this property when clicked.
I hope this gives you an idea on how to start with this.
After a lot of discussion, here is the result to solve this problem:
xaml:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:WpfApplication3"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Horizontal">
<Button Name="btnSinglePlay" Visibility="Collapsed" my:VisibilityAnimation.IsActive="True">SinglePlay</Button>
<Button Name="btnPlay" Click="btnPlay_Click">Play</Button>
<Button Name="btnParallelPlay" Visibility="Collapsed" my:VisibilityAnimation.IsActive="True">ParallelPlay</Button>
</StackPanel>
</Grid>
C# to set the 2 sides button visible.
private void btnPlay_Click(object sender, RoutedEventArgs e)
{
btnSinglePlay.Visibility = Visibility.Visible;
btnParallelPlay.Visibility = Visibility.Visible;
}
And the c# code to permit the fade in/fade out. It comes from WPF Fade Animation so props to Anvaka, not to me.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Animation;
namespace WpfApplication3
{
public class VisibilityAnimation : DependencyObject
{
private const int DURATION_MS = 200;
private static readonly Hashtable _hookedElements = new Hashtable();
public static readonly DependencyProperty IsActiveProperty =
DependencyProperty.RegisterAttached("IsActive",
typeof(bool),
typeof(VisibilityAnimation),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsActivePropertyChanged)));
public static bool GetIsActive(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (bool)element.GetValue(IsActiveProperty);
}
public static void SetIsActive(UIElement element, bool value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(IsActiveProperty, value);
}
static VisibilityAnimation()
{
UIElement.VisibilityProperty.AddOwner(typeof(FrameworkElement),
new FrameworkPropertyMetadata(Visibility.Visible, new PropertyChangedCallback(VisibilityChanged), CoerceVisibility));
}
private static void VisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// So what? Ignore.
}
private static void OnIsActivePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var fe = d as FrameworkElement;
if (fe == null)
{
return;
}
if (GetIsActive(fe))
{
HookVisibilityChanges(fe);
}
else
{
UnHookVisibilityChanges(fe);
}
}
private static void UnHookVisibilityChanges(FrameworkElement fe)
{
if (_hookedElements.Contains(fe))
{
_hookedElements.Remove(fe);
}
}
private static void HookVisibilityChanges(FrameworkElement fe)
{
_hookedElements.Add(fe, false);
}
private static object CoerceVisibility(DependencyObject d, object baseValue)
{
var fe = d as FrameworkElement;
if (fe == null)
{
return baseValue;
}
if (CheckAndUpdateAnimationStartedFlag(fe))
{
return baseValue;
}
// If we get here, it means we have to start fade in or fade out
// animation. In any case return value of this method will be
// Visibility.Visible.
var visibility = (Visibility)baseValue;
var da = new DoubleAnimation
{
Duration = new Duration(TimeSpan.FromMilliseconds(DURATION_MS))
};
da.Completed += (o, e) =>
{
// This will trigger value coercion again
// but CheckAndUpdateAnimationStartedFlag() function will reture true
// this time, and animation will not be triggered.
fe.Visibility = visibility;
// NB: Small problem here. This may and probably will brake
// binding to visibility property.
};
if (visibility == Visibility.Collapsed || visibility == Visibility.Hidden)
{
da.From = 1.0;
da.To = 0.0;
}
else
{
da.From = 0.0;
da.To = 1.0;
}
fe.BeginAnimation(UIElement.OpacityProperty, da);
return Visibility.Visible;
}
private static bool CheckAndUpdateAnimationStartedFlag(FrameworkElement fe)
{
var hookedElement = _hookedElements.Contains(fe);
if (!hookedElement)
{
return true; // don't need to animate unhooked elements.
}
var animationStarted = (bool)_hookedElements[fe];
_hookedElements[fe] = !animationStarted;
return animationStarted;
}
}
}
Canvas has element with adorner. Adorner uses VisualCollection, and places a thumb to specific place of the adorned element. I would like to click to the adorner, hanlde PreviewMouseLeftButtonDown event of the canvas and get the underlying element inside the adorner. But I get MainWindow as e.Source in this case. Could you please help? I'm almost newbie in WPF.
public class ConnectorAdorner : Adorner
{
private readonly ConnectorThumb _thumb;
private readonly VisualCollection _visuals;
public ConnectorAdorner(UIElement adornedElement) : base(adornedElement)
{
Focusable = true;
_visuals = new VisualCollection(this);
_thumb = new ConnectorThumb();
_thumb.Background = Brushes.Transparent;
_visuals.Add(_thumb);
}
protected override Size ArrangeOverride(Size finalSize)
{
double elHeight = AdornedElement.DesiredSize.Height;
double adornerWidth = DesiredSize.Width;
double adornerHeight = DesiredSize.Height;
_thumb.Arrange(new Rect(0, -elHeight/2, adornerWidth, adornerHeight));
return finalSize;
}
protected override Visual GetVisualChild(int index)
{
return _visuals[index];
}
protected override int VisualChildrenCount
{
get { return _visuals.Count; }
}
}
public class Shape : UserControl
{
private AdornerLayer _adornerLayer;
public Shape()
{
Width = 50;
Height = 50;
Background = Brushes.Tomato;
BorderThickness = new Thickness(2);
BorderBrush = Brushes.Black;
this.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
_adornerLayer = AdornerLayer.GetAdornerLayer(this);
if (_adornerLayer != null)
{
ConnectorAdorner adorner = new ConnectorAdorner(this);
_adornerLayer.Add(adorner);
}
}
}
public class ConnectorThumb : Thumb
{
public ConnectorThumb()
{
Width = 20;
Height = 20;
Background = Brushes.Transparent;
Foreground = Brushes.Black;
BorderThickness = new Thickness(1);
IsHitTestVisible = true;
}
}
public partial class MainWindow : Window
{
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// I'd like to get ConnectionThumb here..
}
}
While it is hard to say without seeing your code, but I think using the Canvas.ZIndex property on the elements (Adorner) will ensure that the elements are layered the way your want.
I have a user control that is nested inside a window that is acting as a shell for a dialog display. I ignore focus in the shell window, and in the hosted user control I use the FocusManager to set the initial focus to a named element (a textbox) as shown below.
This works, setting the cursor at the beginning of the named textbox; however I want all text to be selected.
The TextBoxSelectionBehavior class (below) usually does exactly that, but not in this case. Is there an easy xaml fix to get the text in the named textbox selected on initial focus?
Cheers,
Berryl
TextBox Selection Behavior
// in app startup
TextBoxSelectionBehavior.RegisterTextboxSelectionBehavior();
/// <summary>
/// Helper to select all text in the text box on entry
/// </summary>
public static class TextBoxSelectionBehavior
{
public static void RegisterTextboxSelectionBehavior()
{
EventManager.RegisterClassHandler(typeof(TextBox), UIElement.GotFocusEvent, new RoutedEventHandler(OnTextBox_GotFocus));
}
private static void OnTextBox_GotFocus(object sender, RoutedEventArgs e)
{
var tb = (sender as TextBox);
if (tb != null)
tb.SelectAll();
}
}
The hosted UserControl
<UserControl
<DockPanel KeyboardNavigation.TabNavigation="Local"
FocusManager.FocusedElement="{Binding ElementName=tbLastName}" >
<TextBox x:Name="tbLastName" ... />
stop gap solution
Per comments with Rachel below, I ditched the FocusManger in favor of some code behind:
tbLastName.Loaded += (sender, e) => tbLastName.Focus();
Still would love a declarative approach for a simple and common chore though...
I usually use an AttachedProperty to make TextBoxes highlight their text on focus. It is used like
<TextBox local:HighlightTextOnFocus="True" />
Code for attached property
public static readonly DependencyProperty HighlightTextOnFocusProperty =
DependencyProperty.RegisterAttached("HighlightTextOnFocus",
typeof(bool), typeof(TextBoxProperties),
new PropertyMetadata(false, HighlightTextOnFocusPropertyChanged));
[AttachedPropertyBrowsableForChildrenAttribute(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static bool GetHighlightTextOnFocus(DependencyObject obj)
{
return (bool)obj.GetValue(HighlightTextOnFocusProperty);
}
public static void SetHighlightTextOnFocus(DependencyObject obj, bool value)
{
obj.SetValue(HighlightTextOnFocusProperty, value);
}
private static void HighlightTextOnFocusPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var sender = obj as UIElement;
if (sender != null)
{
if ((bool)e.NewValue)
{
sender.GotKeyboardFocus += OnKeyboardFocusSelectText;
sender.PreviewMouseLeftButtonDown += OnMouseLeftButtonDownSetFocus;
}
else
{
sender.GotKeyboardFocus -= OnKeyboardFocusSelectText;
sender.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDownSetFocus;
}
}
}
private static void OnKeyboardFocusSelectText(
object sender, KeyboardFocusChangedEventArgs e)
{
var textBox = e.OriginalSource as TextBox;
if (textBox != null)
{
textBox.SelectAll();
}
}
private static void OnMouseLeftButtonDownSetFocus(
object sender, MouseButtonEventArgs e)
{
TextBox tb = FindAncestor<TextBox>((DependencyObject)e.OriginalSource);
if (tb == null)
return;
if (!tb.IsKeyboardFocusWithin)
{
tb.Focus();
e.Handled = true;
}
}
static T FindAncestor<T>(DependencyObject current)
where T : DependencyObject
{
current = VisualTreeHelper.GetParent(current);
while (current != null)
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
};
return null;
}
Edit
Based on comments below, what about just getting rid of the FocusManager.FocusedElement and setting tb.Focus() and tb.SelectAll() in the Loaded event of your TextBox?
As stated above, you can add an event handler for the Loaded event to set focus and select all text:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
base.DataContext = new Person { FirstName = "Joe", LastName = "Smith" };
base.Loaded += delegate
{
this._firstNameTextBox.Focus();
this._firstNameTextBox.SelectAll();
};
}
}