Dynamically Change the Style of Control in WPF - wpf

I have styles defined for various controls viz. textboxes,combobox,datepickers,etc.. in my application. The resources needed in these styles are present in other styles and all styles are there in ResourceDictionary.
I want to change the background color of these controls to yellow on basis of some configuration settings.
I am thinking to use attached properties and make use of them with styles and set the background color. But not sure how to implement it.
Please suggest if any other approach.
Thanks,
Abdi

You could try this solution:
In your view:
Call your window like 'myForm' or whatever:
I use in this case some radiobuttons. Each of them with an associated color You would adapt this to your requirements.
<UniformGrid Columns="0"
x:Name="_skinChanger"
Grid.Column="0" Grid.Row="1" Width="200">
<RadioButton x:Name="BlueSkin"
Content="Blue Skin" HorizontalAlignment="Left" Width="131" />
<RadioButton x:Name="None"
Content="No Skin"
IsChecked="True" HorizontalAlignment="Left" Width="87" />
<RadioButton x:Name="RedSkin"
Content="Red Skin" Margin="0,0,0,4" HorizontalAlignment="Left" Width="109" />
<RadioButton x:Name="GreenSkin"
Content="Green Skin" HorizontalAlignment="Left" Width="109" />
</UniformGrid>
The elements you want to change the style should use dynamic resource:
...
Style="{DynamicResource LabelStyle}"
...
Inside the constructor of your window for example...
Loaded += RuntimeSkinning_Loaded;
Then define the event in your class.
void RuntimeSkinning_Loaded(object sender, RoutedEventArgs e)
{
// this is my uniformgrid where the radiobuttons are placed. I add a handled for
// the checkedevent.
_skinChanger.AddHandler(RadioButton.CheckedEvent, new RoutedEventHandler(OnSkinChanged));
}
private void OnSkinChanged(object sender, RoutedEventArgs e)
{
string name = (e.OriginalSource as RadioButton).Name;
_registerForm.Resources.Clear();
_registerForm.Resources.MergedDictionaries.Clear();
if (name == "None") return;
ResourceDictionary skin =
Application.LoadComponent(new Uri("/myproject;component/Skins/" + name + ".xaml", UriKind.Relative)) as ResourceDictionary;
_registerForm.Resources.MergedDictionaries.Add(skin);
e.Handled = true;
}

Related

Access keys not working inside popup (WPF)

I have a simple popup in my WPF application.
There is a button with access-key inside this popup.
The problem I have is that this button doesn't respond to Alt+access-key combination.
Moreover pressing Alt doesn't make access key visible like it happens in ordinary window.
Is there any way to make controls inside popup respond to Alt+access-key combination?
P.S. I have no problem with navigation using Tab through this popup.
Sapmle code that I'm using
<Grid>
<Button Click="ButtonBase_OnClick" Content="_Open File"></Button>
<Popup x:Name="Popup" StaysOpen="False">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button Content="_Open File"/>
<Button Grid.Row="1" Content="O_pen File"/>
<CheckBox Grid.Row="2" Content="_Go"></CheckBox>
</Grid>
</Popup>
</Grid>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Popup.IsOpen = true;
}
I have also tried adding this as the first answer suggests
private void Popup_OnOpened(object sender, EventArgs e)
{
var popup = sender as Popup;
popup.Child.Focusable = true;
Keyboard.Focus(popup.Child);
}
I have also tried the idea from the first comment
private void Popup_OnOpened(object sender, EventArgs e)
{
var popup = sender as Popup;
FocusManager.SetIsFocusScope(popup, true);
}
or instead of standart focus scope the one from suggested link codeproject.com/Articles/38507/Using-the-WPF-FocusScope
EnhancedFocusScope.SetFocusOnActiveElementInScope(popup);
Setting focus scope helped a little bit, but I didn't manage to make it work exactly as I would like.
Setting focus scope to true did help to use alt+key combination for checkboxes and label+textbox, but not for buttons. Although I could use Alt+access key combinations, I couldn't actually see them, because underscores didn't appear when I pressed Alt
Popup is not part of the visual tree. This means it has its own isolated focus scope. When a Popup is opened, the Popup.Child is hosted in a dedicated Window with its own detached visual tree. The Popup.Child therefore needs to explicitly receive keyboard focus before the access keys are available in the detached focus scope.
You can handle the Popup.Opened event, either in code-behind or using an attached behavior.
It's essential that the Popup.Child is focusable in order to receive keyboard focus.
Some classes like Panel and its subclasses have UIElement.Focusable set to false by default.
<StackPanel>
<ToggleButton x:Name="ToggleButton" Content="Show Popup" />
<Popup x:Name="Popup"
AllowsTransparency="True"
PlacementTarget="{Binding ElementName=ToggleButton}"
IsOpen="{Binding ElementName=ToggleButton, Path=IsChecked}"
Opened="Popup_OnOpened">
<StackPanel>
<Button Grid.Row="1" Content="O_pen File" />
<CheckBox Grid.Row="2" Content="_Go" />
</StackPanel>
</Popup>
</StackPanel>
private void Popup_OnOpened(object sender, EventArgs e)
{
var popup = sender as Popup;
popup.Child.Focusable = true;
Keyboard.Focus(popup.Child);
}
Once a button or a checkbox inside the popup is focused, the alt-shortcuts works.
<Button Click="ButtonBase_OnClick" Content="_Open File" />
<Popup x:Name="Popup">
<StackPanel Background="White">
<CheckBox x:Name="FirstCeckbox" Content="_Foo" />
<CheckBox Content="_Bar" />
</StackPanel>
</Popup>
Code behind:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Popup.IsOpen = !Popup.IsOpen;
if (Popup.IsOpen)
FirstCeckbox.Focus(); // Make sure to focus a Button or a Checkbox, not the Stackpanel or Grid etc.
}

How to change two buttons' colors in a stackpanel

I have two buttons in a stackpanel and I want to change one's background and other's foreground to the same color.
I tried this in xaml.
<StackPanel
Style="{DynamicResource LeftStackPanel}"
MouseEnter="StackPanel_MouseEnter"
MouseLeave="StackPanel_MouseLeave"
>
<Button
x:Name="profile_rct"
HorizontalAlignment="Center"
Height="70"
VerticalAlignment="Center"
Width="7"
Background="{Binding
Path=BackgroundBtn,
Mode=OneWay}"
BorderThickness="1,1,1,1"
BorderBrush="DimGray" />
<Button
x:Name="profile_txt"
Content="Profil Bilgisi"
Style="{DynamicResource LeftButton}"
Foreground="{Binding
Path=BackgroundBtn,
Mode=OneWay}">
<Button.LayoutTransform>
<RotateTransform Angle="90" />
</Button.LayoutTransform>
</Button>
</StackPanel>
In LeftStackPanel it only has positional arguments, so I don't think it is important. In profile_rct I bind background to BackgroundBtn and in profile_txt I bind the same variable to foreground this time. In LeftButton style there is no value for foreground.
In code here is StackPanel_MouseEnter and BackgroundBtn codes.
#region ViewModelProperty
private Brush backgroundbtn;
public Brush Backgroundbtn
{
get
{
return backgroundbtn;
}
set
{
backgroundbtn = value;
OnPropertyChanged("Backgroundbtn");
}
}
private void OnPropertyChanged(string v)
{
//throw new NotImplementedException();
}
#endregion
private void StackPanel_MouseEnter(object sender, MouseEventArgs e)
{
Backgroundbtn = Brushes.CadetBlue;
}
There are two problems here, even though I write the event on stackpanel, color of items only changes when I point them and only the color of pointed item is changing. I want to change colors of both items.
Second is it doesn't change foreground of profile_txt but background. I didn't understand that.
Learn binding in WPF: http://www.cheat-sheets.org/saved-copy/WpfBinding.pdf
In your case:
Background="{Binding ElementName=BackgroundBtn, Path=Background, Mode=OneWay}"
The first thing that stands out is a difference in case: BackgroundBtn is not a property on your ViewModel, but Backgroundbtn is.
Is that the issue?

Silverlight HyperlinkButton styles in custom control

I have a strange styling problem that I cannot figure out.
What I want to achieve is that HyperlinkButtons in datagrid cells are styled with an underline when the mouse hovers the link.
I have one column where I declare the HyperlinkButton element in XAML like this:
<HyperlinkButton Style="{StaticResource HyperlinkButtonStyle}" DataContext="{Binding}" FontSize="11" Content="{Binding DguNr}" Click="DgunrHyperlinkButtonClick" />
This works fine - the link is styled as I want.
In another column, I need to display n number of HyperlinkButtons based on some information in the bound element. Hence I have created a usercontrol that will render 0..n Hyperlinkbuttons. The control is declared in XAML like this:
<sdk:DataGridTemplateColumn IsReadOnly="True" CanUserSort="True">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<bc:BoreholePlantGridColumn Plants="{Binding Plants, Mode=OneWay}" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
The code behind for the control looks like this:
public partial class BoreholePlantGridColumn : UserControl, INotifyPropertyChanged
{
public BoreholePlantGridColumn()
{
InitializeComponent();
Loaded += new RoutedEventHandler(BoreholePlantGridColumn_Loaded);
}
void BoreholePlantGridColumn_Loaded(object sender, RoutedEventArgs e)
{
var borehole = (SelectableBoring)this.DataContext;
foreach(var p in borehole.Plants)
{
// <HyperlinkButton HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" DataContext="{Binding}" Foreground="Black" FontSize="11" Content="{Binding DguNr}" Click="DgunrHyperlinkButtonClick" />
var button = new HyperlinkButton();
button.Content = p.PlantId;
button.Style = (Style)App.Current.Resources["HyperlinkButtonStyle"];
button.VerticalContentAlignment = VerticalAlignment.Center;
var url = String.Format(Common.Constants.Url.GeusPlantLinkTemplate, p.PlantId);
button.NavigateUri = new Uri(url, UriKind.RelativeOrAbsolute);
button.TargetName = "_blank";
LayoutRoot.Children.Add(button);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public static readonly DependencyProperty PlantsProperty = DependencyProperty.Register("Value", typeof(Anlaeg), typeof(BoreholePlantGridColumn), new PropertyMetadata(new PropertyChangedCallback(ValueChanged)));
public IList<Anlaeg> Plants
{
get { return (IList<Anlaeg>)this.GetValue(PlantsProperty); }
set { this.SetValue(PlantsProperty, value); }
}
private static void ValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var myUC = (BoreholePlantGridColumn)obj;
var newValue = (IList<Anlaeg>)e.NewValue;
}
}
This works almost as expected; the linkbuttons(s) are rendered with the correct color - but there is not displayed an underline text when the mouse hovers the link.
I do not understand why the underline is displayed in the hyperlinkbutton that is declared directly in XAML but not in the hyperlink that is rendered in code-behind. Can someone please help me on this?
I have used the hyperlinkbutton styling from this thread:1
I just use the Following Code
<ListBox Grid.Column="1" ItemsSource="{Binding EncyclopediaList}" HorizontalContentAlignment ="Stretch" Margin="5,0" >
<ListBox.ItemTemplate>
<DataTemplate>
<HyperlinkButton Content="{Binding Name}" Foreground="Black" Command="{Binding ViewArticlePageCommand, Source={StaticResource EncyclopediaViewModel}}" CommandParameter="{Binding ServerEncyclopediaID}" FontFamily="Arial" FontSize="18" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
No need to Style the HyperlinkButton its Works perfectly in My Case Add the Underline and Make it bold when Mouse move over it.

Silverlight: Define event handler in hierarchical data template

I am having problems getting at a click event of a button and am using Silverlight 3.0 w/ matching Silverlight Toolkit.
Problem
I have this TreeView:
TreeView sample http://a.imagehost.org/0939/TreeView.png.
The value for a certain node is the sum of the values of its children. Only in leaves can data be added (for the time being).
What I want to achieve is that a user can add (and eventually remove) entries in the tree to eventually create a custom diagram.
To that end I would like the "plus sign" to insert a new line / node as child of the node where the user clicked. (I.e. if I click the plus at "Display", I get a line below to specify CRT or TFT or whatever.)
Thing is, for all my brain is worth, I don't know how to receive any useful event.
The TextBlock, TextBox and Button are defined in a hierarchical template and I can't define a Click-handler in that template.
OTOH, I haven't found a way to get at the template items of a certain TreeViewItem from within (c#) code. Very well am I able to do trv.ItemContainerGenerator.GetContainerFromItem(item), and as Justin Angel showed I can very well change the font size, but didn't find any way to access the textbox or button.
Is there any way to capture the click event to the button? Or any alternative way of getting something that gives the "add below" functionality?
Thank you in advance.
More Data
The treeview xaml is this:
<controls:TreeView x:Name="SankeyDataTree"
ItemTemplate="{StaticResource SankeyTreeTemplate}" BorderThickness="0"
Background="{x:Null}" HorizontalAlignment="Left" VerticalAlignment="Top">
<controls:TreeViewItem IsExpanded="True">
<controls:TreeViewItem.HeaderTemplate>
<DataTemplate>
<TextBlock Text="Loading..."/>
</DataTemplate>
</controls:TreeViewItem.HeaderTemplate>
</controls:TreeViewItem>
</controls:TreeView>
I use this HierarchicalDataTemplate (and stole the appraoch from Timmy Kokke):
<Data:HierarchicalDataTemplate x:Key="SankeyTreeTemplate" ItemsSource="{Binding Children}">
<Grid Height="24">
<Grid.ColumnDefinitions>
<!-- ... -->
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Value.name, Mode=TwoWay}" VerticalAlignment="Center"/>
<TextBox Text="{Binding Path=Value.flow, Mode=TwoWay}" Margin="4,0" VerticalAlignment="Center" d:LayoutOverrides="Width" Grid.Column="1" TextAlignment="Right" Visibility="{Binding Children, Converter={StaticResource BoxConverter}, ConverterParameter=\{box\}}"/>
<TextBlock Text="{Binding Path=Value.throughput, Mode=TwoWay}" Margin="4,0" VerticalAlignment="Center" d:LayoutOverrides="Width" Grid.Column="1" TextAlignment="Right" Visibility="{Binding Children, Converter={StaticResource BoxConverter}, ConverterParameter=\{block\}}"/>
<Button Margin="0" Grid.Column="2" Style="{StaticResource TreeViewItemButtonStyle}">
<Image Source="../Assets/add.png" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Button>
</Grid>
</Data:HierarchicalDataTemplate>
To this TreeView is bound a "SimpleTree", whose Values hold basically onle a string (name) and two doubles (flow and throughput).
public String name { get; set; }
public Double flow { get; set; }
public Double throughput { get; set; }
(Plus the code for the INotifyPropertyChanged to get a twoway bind to the text boxes.)
You can attach a Behavior to the Button in the HierarchicalDataTemplate and let that handle Click events from the Button.
Download and install the Expression Blend 3 SDK. Add a reference to System.Windows.Interactivity in the project and add a Behavior attached to a Button:
public class ButtonClickBehavior : Behavior<Button> {
protected override void OnAttached() {
base.OnAttached();
AssociatedObject.Click += ButtonClick;
}
protected override void OnDetaching() {
base.OnDetaching();
AssociatedObject.Click -= ButtonClick;
}
void ButtonClick(object sender, RoutedEventArgs e) {
Node node = AssociatedObject.DataContext as Node;
if (node != null) {
// Button clicked. Do something to associated node.
}
}
}
Attach the Behavior to the Button in the HierarchicalDataTemplate (assuming this namespace declaration: xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"):
<Button Margin="0" Grid.Column="2" Style="{StaticResource TreeViewItemButtonStyle}">
<Image Source="../Assets/add.png" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<interactivity:Interaction.Behaviors>
<local:ButtonClickBehavior/>
</interactivity:Interaction.Behaviors>
</Button>
If desired you can add properties to ButtonClickBehavior and set those from XAML to create a more reusable Behavior.
You can handle the button click event in the code behind. To get to the data, just bind it to the Tag attribute.
<Button Margin="0" Grid.Column="2"
Click="Button_Click" Tag="{Binding}"
Style="{StaticResource TreeViewItemButtonStyle}">
<Image Source="../Assets/add.png" Margin="0"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Button>
In the code behind, handle it and access the element.
private void Button_Click(object sender, RoutedEventArgs e)
{
var data = ((Button)sender).Tag as SimpleTreeNode
}
Where SimpleTreeNode is the name of your tree element class.
You should be able to add a new node to the data found now.

WPF and initial focus

It seems that when a WPF application starts, nothing has focus.
This is really weird. Every other framework I've used does just what you'd expect: puts initial focus on the first control in the tab order. But I've confirmed that it's WPF, not just my app -- if I create a new Window, and just put a TextBox in it, and run the app, the TextBox doesn't have focus until I click on it or press Tab. Yuck.
My actual app is more complicated than just a TextBox. I have several layers of UserControls within UserControls. One of those UserControls has Focusable="True" and KeyDown/KeyUp handlers, and I want it to have the focus as soon as my window opens. I'm still somewhat of a WPF novice, though, and I'm not having much luck figuring out how to do this.
If I start my app and press the Tab key, then focus goes to my focusable control, and it starts working the way I want. But I don't want my users to have to hit Tab before they can start using the window.
I've played around with FocusManager.FocusedElement, but I'm not sure which control to set it on (the top-level Window? the parent that contains the focusable control? the focusable control itself?) or what to set it to.
What do I need to do to get my deeply-nested control to have initial focus as soon as the window opens? Or better yet, to focus the first focusable control in the tab order?
This works, too:
<Window FocusManager.FocusedElement="{Binding ElementName=SomeElement}">
<DataGrid x:Name="SomeElement">
...
</DataGrid>
</Window>
I had the bright idea to dig through Reflector to see where the Focusable property is used, and found my way to this solution. I just need to add the following code to my Window's constructor:
Loaded += (sender, e) =>
MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
This will automatically select the first control in the tab order, so it's a general solution that should be able to be dropped into any window and Just Work.
Based on the accepted answer implemented as an attached behavior:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace UI.Behaviors
{
public static class FocusBehavior
{
public static readonly DependencyProperty FocusFirstProperty =
DependencyProperty.RegisterAttached(
"FocusFirst",
typeof(bool),
typeof(FocusBehavior),
new PropertyMetadata(false, OnFocusFirstPropertyChanged));
public static bool GetFocusFirst(Control control)
{
return (bool)control.GetValue(FocusFirstProperty);
}
public static void SetFocusFirst (Control control, bool value)
{
control.SetValue(FocusFirstProperty, value);
}
static void OnFocusFirstPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
Control control = obj as Control;
if (control == null || !(args.NewValue is bool))
{
return;
}
if ((bool)args.NewValue)
{
control.Loaded += (sender, e) =>
control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
}
Use it like this:
<Window xmlns:Behaviors="clr-namespace:UI.Behaviors"
Behaviors:FocusBehavior.FocusFirst="true">
I found another possible solution. Mark Smith posted a FirstFocusedElement markup extension for use with FocusManager.FocusedElement.
<UserControl x:Class="FocusTest.Page2"
xmlns:FocusTest="clr-namespace:FocusTest"
FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">
After having a 'WPF Initial Focus Nightmare' and based on some answers on stack, the following proved for me to be the best solution.
First, add your App.xaml OnStartup() the followings:
EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent,
new RoutedEventHandler(WindowLoaded));
Then add the 'WindowLoaded' event also in App.xaml :
void WindowLoaded(object sender, RoutedEventArgs e)
{
var window = e.Source as Window;
System.Threading.Thread.Sleep(100);
window.Dispatcher.Invoke(
new Action(() =>
{
window.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}));
}
The threading issue must be use as WPF initial focus mostly fails due to some framework race conditions.
I found the following solution best as it is used globally for the whole app.
Hope it helps...
Oran
Had same problem solved it with simple solution:
In the main window:
<Window ....
FocusManager.FocusedElement="{Binding ElementName=usercontrolelementname}"
... />
In the user control:
private void UserControl_GotFocus_1(object sender, RoutedEventArgs e)
{
targetcontrol.Focus();
this.GotFocus -= UserControl_GotFocus_1; // to set focus only once
}
You can easily have the control set itself as the focused element in XAML.
<Window>
<DataGrid FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">
...
</DataGrid>
</Window>
I've never tried setting this in a usercontrol and seeing if this works, but it may.
A minimal version of Mizipzor's answer for C# 6+.
public static class FocusBehavior
{
public static readonly DependencyProperty GiveInitialFocusProperty =
DependencyProperty.RegisterAttached(
"GiveInitialFocus",
typeof(bool),
typeof(FocusBehavior),
new PropertyMetadata(false, OnFocusFirstPropertyChanged));
public static bool GetGiveInitialFocus(Control control) => (bool)control.GetValue(GiveInitialFocusProperty);
public static void SetGiveInitialFocus(Control control, bool value) => control.SetValue(GiveInitialFocusProperty, value);
private static void OnFocusFirstPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var control = obj as Control;
if (control == null || !(args.NewValue is bool))
return;
if ((bool)args.NewValue)
control.Loaded += OnControlLoaded;
else
control.Loaded -= OnControlLoaded;
}
private static void OnControlLoaded(object sender, RoutedEventArgs e) => ((Control)sender).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
Use in your XAML:
<Window local:FocusBehavior.GiveInitialFocus="True" />
Above solution was not working as expected for me, I've changed slightly the behavior proposed by Mizipzor as following:
From this part
if ((bool)args.NewValue)
{
control.Loaded += (sender, e) =>
control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
To this
if ((bool)args.NewValue)
{
control.Loaded += (sender, e) => control.Focus();
}
ANd I'm not attaching this behavior to Window or UserControl, but to control I want to focus initially, e.g.:
<TextBox ui:FocusBehavior.InitialFocus="True" />
Oh, sorry for different naming I'm using InitialFocus name for the attached property.
And this is working for me, maybe it could help someone else.
If you are like me, and you are using some frameworks that, somehow, mess up with the basic focus behaviors, and make all solutions above irrelevant, you can still do this :
1 - Note the element which get the focus (whatever it is!)
2 - Add this in your code behind xxx.xaml.cs
private bool _firstLoad;
3 - Add this on the element which get the first focus :
GotFocus="Element_GotFocus"
4 - Add the Element_GotFocus method in the code behind, and specify the WPF named element who need the first focus :
private void Element_GotFocus(object sender, RoutedEventArgs e)
{
if(_firstLoad)
{
this.MyElementWithFistFocus.Focus();
_firstLoad = false;
}
}
5 - Manage the Loaded event
in XAML
Loaded="MyWindow_Loaded"
in xaml.cs
private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
_firstLoad = true;
this.Element_GotFocus(null, null);
}
Hope this will help as a last resort solution
I also faced the same problem. I had three text boxes inside canvas container and wanted the first text box to be focused when the user control opens. WPF code followed MVVM pattern. I created a separate behavior class for focusing the element and binded it to my view like this.
Canvas behavior code
public class CanvasLoadedBehavior : Behavior<Canvas>
{
private Canvas _canvas;
protected override void OnAttached()
{
base.OnAttached();
_canvas = AssociatedObject as Canvas;
if (_canvas.Name == "ReturnRefundCanvas")
{
_canvas.Loaded += _canvas_Loaded;
}
}
void _canvas_Loaded(object sender, RoutedEventArgs e)
{
FocusNavigationDirection focusDirection = FocusNavigationDirection.Next;
// MoveFocus takes a TraveralReqest as its argument.
TraversalRequest request = new TraversalRequest(focusDirection);
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
if (elementWithFocus != null)
{
elementWithFocus.MoveFocus(request);
}
}
}
Code for view
<Canvas Name="ReturnRefundCanvas" Height="200" Width="1466" DataContext="{Binding RefundSearchViewModel}">
<i:Interaction.Behaviors>
<b:CanvasLoadedBehavior />
</i:Interaction.Behaviors>
<uc:Keyboard Canvas.Left="973" Canvas.Top="111" ToolTip="Keyboard" RenderTransformOrigin="-2.795,9.787"></uc:Keyboard>
<Label Style="{StaticResource Devlbl}" Canvas.Left="28" Content="Return and Refund Search" Canvas.Top="10" />
<Image Height="30" Width="28" Canvas.Top="6" Canvas.Left="5" Source="pack://application:,,,/HomaKiosk;component/images/searchF.png">
<Image.OpacityMask>
<ImageBrush ImageSource="pack://application:,,,/HomaKiosk;component/images/searchF.png"/>
</Image.OpacityMask>
</Image>
<Separator Height="4" Canvas.Left="6" Margin="0" Canvas.Top="35" Width="1007"/>
<ContentControl Canvas.Top="45" Canvas.Left="21"
ContentTemplate="{StaticResource ErrorMsg}"
Visibility="{Binding Error, Converter={c:StringNullOrEmptyToVisibilityConverter}}"
Content="{Binding Error}" Width="992"></ContentControl>
<Label Style="{StaticResource Devlbl}" Canvas.Left="29" Name="FirstName" Content="First Name" Canvas.Top="90" />
<wpf:AutoCompleteTextBox Style="{StaticResource AutoComp}" Height="32" Canvas.Left="33" ToolTip="First Name" Canvas.Top="120" Width="205" Padding="10,5" TabIndex="1001"
VerticalAlignment="Top"
Watermark=""
IconPlacement="Left"
IconVisibility="Visible"
Delay="100"
Text="{Binding FirstName, Mode=TwoWay, TargetNullValue=''}"
Provider="{Binding FirstNameSuggestions}">
<wpf:AutoCompleteTextBox.ItemTemplate>
<DataTemplate>
<Border Padding="5">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding}"
FontWeight="Bold" />
</StackPanel>
</Border>
</DataTemplate>
</wpf:AutoCompleteTextBox.ItemTemplate>
</wpf:AutoCompleteTextBox>
<Label Style="{StaticResource Devlbl}" Canvas.Left="250" Content="Last Name" Canvas.Top="90" />
<wpf:AutoCompleteTextBox Style="{StaticResource AutoComp}" Height="32" ToolTip="Last Name" Canvas.Left="250" Canvas.Top="120" Width="205" Padding="10,5" TabIndex="1002"
VerticalAlignment="Top"
Watermark=""
IconPlacement="Left"
IconVisibility="Visible"
Delay="100"
Text="{Binding LastName, Mode=TwoWay, TargetNullValue=''}"
Provider="{Binding LastNameSuggestions}">
<wpf:AutoCompleteTextBox.ItemTemplate>
<DataTemplate>
<Border Padding="5">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding}"
FontWeight="Bold" />
</StackPanel>
</Border>
</DataTemplate>
</wpf:AutoCompleteTextBox.ItemTemplate>
</wpf:AutoCompleteTextBox>
<Label Style="{StaticResource Devlbl}" Canvas.Left="480" Content="Receipt No" Canvas.Top="90" />
<wpf:AutoCompleteTextBox Style="{StaticResource AutoComp}" Height="32" ToolTip="Receipt No" Canvas.Left="480" Canvas.Top="120" Width="205" Padding="10,5" TabIndex="1002"
VerticalAlignment="Top"
Watermark=""
IconPlacement="Left"
IconVisibility="Visible"
Delay="100"
Text="{Binding ReceiptNo, Mode=TwoWay, TargetNullValue=''}"
Provider="{Binding ReceiptIdSuggestions}">
<wpf:AutoCompleteTextBox.ItemTemplate>
<DataTemplate>
<Border Padding="5">
<StackPanel Orientation="Vertical" >
<TextBlock Text="{Binding}"
FontWeight="Bold">
</TextBlock>
</StackPanel>
</Border>
</DataTemplate>
</wpf:AutoCompleteTextBox.ItemTemplate>
<i:Interaction.Behaviors>
<b:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9]+$" MaxLength="15" />
</i:Interaction.Behaviors>
</wpf:AutoCompleteTextBox>
<!--<Label Style="{StaticResource Devlbl}" Canvas.Left="710" Content="Duration" Canvas.Top="79" />-->
<!--<ComboBox AllowDrop="True" Canvas.Left="710" ToolTip="Duration" Canvas.Top="107" Width="205" TabIndex="1004"
Style="{StaticResource CommonComboBox}"
ItemsSource="{Binding Durations}" DisplayMemberPath="Description" SelectedValuePath="Id" SelectedValue="{Binding SelectedDate, Mode=TwoWay}">
</ComboBox>-->
<Button Content="Search" Style="{StaticResource MyButton}" ToolTip="Search"
Canvas.Top="116" Canvas.Left="710" Cursor="Hand"
Command="{Binding SearchCommand}" TabIndex="2001">
</Button>
<Button Content="Clear" Style="{StaticResource MyButton}" ToolTip="Clear"
Canvas.Top="116" Canvas.Left="840" Cursor="Hand"
Command="{Binding ClearCommand}" TabIndex="2002">
</Button>
<Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="25" Source="pack://application:,,,/HomaKiosk;component/images/chkpending.png"/>
<Label Style="{StaticResource LegendLbl}" Canvas.Left="50" Content="Check Returned and Payment Pending" Canvas.Top="178" />
<Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="300" Source="pack://application:,,,/HomaKiosk;component/images/chkrepaid.png"/>
<Label Style="{StaticResource LegendLbl}" Canvas.Left="325" Content="Repaid" Canvas.Top="178" />
<Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="395" Source="pack://application:,,,/HomaKiosk;component/images/refund.png"/>
<Label Style="{StaticResource LegendLbl}" Canvas.Left="415" Content="Refunded" Canvas.Top="178" />
</Canvas>
<Window FocusManager.FocusedElement="{Binding ElementName=yourControlName}">

Resources