Control Focus in dialog box with Prism 7.x and 8.x? - wpf

I need to set a TextBox being focused when the dialog box was opened. It was working with Prism 6.x; but is no longer working since 7.x and 8.x. The framework difference is that Prism 6.x uses InteractionRequest for dialog box; while Prism 7/8 uses the dialog service. Does Prism 7 and 8 also introduce some new approaches related to dialog box? Following is the code snippet of XAML setting FocusManager:
<UserControl x:Class="FeatureModule.Views.MyDialogView"
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:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
FocusManager.FocusedElement="{Binding ElementName=edit}"
mc:Ignorable="d" d:DesignHeight="140" d:DesignWidth="400">
It does not function any more. Does it related to the dialog service?

The dialog service starting from Prism 7.2.x is completely different from the legacy interaction requests in prior versions, so it is hard to tell what exactly was the breaking change. Setting the focus manager property on the root element of the UserControl instead of on the UserControl could solve this the issue, but it might not work under other circumstances, read below why.
<UserControl x:Class="FeatureModule.Views.MyDialogView" ...>
<Grid FocusManager.FocusedElement="{Binding ElementName=edit}">
<!-- ...your markup -->
</Grid>
</UserControl>
I think the issue is more general. WPF has two different focus concepts, logical and keyboard focus. What you want to set on the TextBox is keyboard focus, but the FocusManager only sets logical focus.
Logical focus pertains to the FocusManager.FocusedElement within a specific focus scope.
The important part is the relationship between logical and keyboard focus.
An element with logical focus does not necessarily have keyboard focus, but an element with keyboard focus will have logical focus.
This means, even if you set the logical focus on a different element it might not work.
Keyboard focus pertains to the element that is currently receiving keyboard input. There can be only one element with keyboard focus.
Unfortunately, the Keyboard.FocusedElement property has only a getter and is not a dependency property, so you cannot bind to it, but you can call the Keyboard.Focus method instead. In order to avoid code-behind, you can either create a small behavior or simply an attached property like this:
public static class KeyboardFocus
{
public static readonly DependencyProperty ElementProperty = DependencyProperty.RegisterAttached(
"Element", typeof(FrameworkElement), typeof(KeyboardFocus), new PropertyMetadata(null, OnElementChanged));
public static FrameworkElement GetElement(DependencyObject dependencyObject)
{
return (FrameworkElement)dependencyObject.GetValue(ElementProperty);
}
public static void SetElement(DependencyObject dependencyObject, FrameworkElement value)
{
dependencyObject.SetValue(ElementProperty, value);
}
private static void OnElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is FrameworkElement frameworkElement)
frameworkElement.Loaded += OnLoaded;
}
private static void OnLoaded(object sender, RoutedEventArgs e)
{
var frameworkElement = (FrameworkElement)sender;
frameworkElement.Loaded -= OnLoaded;
Keyboard.Focus(frameworkElement);
}
}
This attached property can be used on any FrameworkElement, not only TextBox. You can customize it to fit your needs. In XAML apply it to the root element of your UserControl, not the UserControl itself, otherwise the binding element might not be found e.g.:
<UserControl x:Class="FeatureModule.Views.MyDialogView"
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:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:FeatureModule.Views"
prism:ViewModelLocator.AutoWireViewModel="True"
FocusManager.FocusedElement="{Binding ElementName=edit}"
mc:Ignorable="d" d:DesignHeight="140" d:DesignWidth="400">
<Grid local:KeyboardFocus.Element="{Binding ElementName=edit}">
<!-- ...your markup. -->
</Grid>
</UserControl>
Alternatively, you can set the property on the TextBox itself.
<TextBox local:KeyboardFocus.Element="{Binding RelativeSource={RelativeSource Self}}"/>

Related

Xaml: design a layout with dynamically visible components

In a mvvm application some areas inside a window (in reality it is a UserControl inside MainWindow) are dynamically displayed according to the user selections.
The changing blocks are inside Stackpanels, I have 4 of them and only one at a time is displayed. This is accomplished binding Visibility to a bool property and using the BooleanToVisibilityConverter.
I put all the alternate StackPanel inside parent control. It works correctly, but during design phase in Visual Studio I see all of them, so I have problems in figuring the final layout.
How can I easily create the layout having more controls which share the same window area and are displayed one at a time ?
Setting A Design Time Only Data Context
Developing XAML in the Studio Designer can be greatly simplified by setting the Design-Time Data Context.
One implementation is based on setting a duplicate DataContext which will be ignored during the final compilation.
To implement the switching, add to the ViewModel, a property that will inform the designer whether it can be used in Development Mode or not.
I use an MVVMLight situation for this example, but for this declared instance property IsInDesignMode and static property ViewModelBase.IsInDesignModeStatic.
Example:
using System.ComponentModel;
namespace DataContextDesignTime.Example
{
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _flag;
public bool Flag
{
get => _flag;
set
{
if (!Equals(_flag, value))
{
_flag = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Flag)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NotFlag)));
}
}
}
public bool NotFlag => !Flag;
}
}
<Window x:Class="DataContextDesignTime.Example.ExamleWindow"
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:DataContextDesignTime.Example"
mc:Ignorable="d"
Title="ExamleWindow" Height="450" Width="800">
<d:Window.DataContext>
<local:MyViewModel Flag="True" NotFlag="True"/>
</d:Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<Border Background="LightBlue" Height="200"
Visibility="{Binding Flag, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Border Background="LightGreen" Height="400"
Visibility="{Binding NotFlag, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
In this example, you can change property values in XAML or in the Property Browser.
And you will immediately see the work of your bindings, triggers, how the display for certain data changes.
Note
This may fail on more complex VMs/packages, but in general by setting the DataContext at design time is not difficult.
I need to recompile the project to see the changes in the properties.
The XAML Designer panel has an «Enable/Disable Project Code» button.
, but during design phase in Visual Studio I see all of them, so I have problems in figuring the final layout.
This problem is easily resolved by bringing up the Document Outline tab in visual studio. Once open, navigate to the visible tree and toggle the eyeball to visibly hide/unhide the control[s] one is not interested in; during design time only.

Problem with ItemsSource binding in Silverlight ListBox

I'm trying to list some strings in a Silverlight ListBox. I'm binding a vanilla List to the ItemsSource and then specifying the property of the List item to display in DisplayMemberPath. There is something specific to my implementation that causes the ListBox to display the templated items instead of the property specified inside those items.
Here's the scenario. I have a Parent class that derives from UserControl that adds a "Title" Dependency Property. I create a few Child controls that derive from Parent and specify that inherited Title property. For some reason, binding to that Title property in the ListBox causes the unexpected behavior. Here's the code:
public class Parent : UserControl
{
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(Parent), new PropertyMetadata(String.Empty));
}
The Child XAML code (Child1 and Child2 are basically the same XAML with trivial codebehinds)
<v:Parent x:Class="TemplateBindingTest.Child1"
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:v="clr-namespace:TemplateBindingTest"
mc:Ignorable="d" Title="Baby 1" Height="41" Width="94">
<Grid x:Name="LayoutRoot" Background="#FFFFBBBB">
<TextBlock HorizontalAlignment="Center" Text="I da baby" VerticalAlignment="Center" FontSize="14" />
</Grid>
The "ViewModel"
public class TheViewModel : INotifyPropertyChanged
{
public List<Parent> Babies { get; set; }
public TheViewModel()
{
Babies = new List<Parent>();
Child1 baby1 = new Child1();
Child2 baby2 = new Child2();
Babies.Add(baby1);
Babies.Add(baby2);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainPage.xaml
<UserControl x:Class="TemplateBindingTest.MainPage"
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"
mc:Ignorable="d" xmlns:my="clr-namespace:TemplateBindingTest" Height="268" Width="355">
<Grid x:Name="LayoutRoot" Background="White">
<ListBox ItemsSource="{Binding Path=Babies}" DisplayMemberPath="Title" Height="219" HorizontalAlignment="Left" Margin="12,12,0,0" VerticalAlignment="Top" Width="180" />
<my:Child1 HorizontalAlignment="Left" Margin="203,26,0,0" x:Name="child1" VerticalAlignment="Top" />
<my:Child2 HorizontalAlignment="Left" Margin="203,92,0,0" x:Name="child2" VerticalAlignment="Top" />
</Grid>
So ignoring the fact that it's a bit weird to maintain a list of UI controls in a "viewmodel" class, this is all fairly simple Silverlight. In the ListBox control on MainPage I would expect to see the title for each Child control. Instead, the Child controls themselves show up in the ListBox. What am I missing here? I find it very odd that Silverlight just decides to render the Children controls with no complaints in the Debug Output or other error messages. It's like the DisplayMemberPath attribute gets completely ignored. Could this be a bug in Silverlight?
For ease of testing, here's a link to the full Visual Studio project containing the code above.
This behaviour seems to be by design. If the listbox sees that the Content is a derivative of a UIElement then it makes the simple (and I think reasonable) assumption that you intend for that content to be displayed.
You are right what you are doing is "a bit weird" and you are paying the price. This answer may hold a solution for you. However I would recommend you review the choice to hold an instance of a UserControl in the viewmodel.
#AnthonyWJones is correct and the link he provided led to this answer: I would need to implement my own hacked up version of ListBox in order to achieve the desired functionality mentioned in my question.
public class MyListBox : ListBox
{
protected override bool IsItemItsOwnContainerOverride(object item)
{
return false;
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
((ListBoxItem)element).ContentTemplate = ItemTemplate;
}
}
I guess the moral of this story is don't try to deal with UI elements directly in your ViewModel classes. It's not best practice (and definitely not MVVM) for a reason.

Change a textbox's text value on a UserControl from a Window in WPF

I have a UserControl (called Invoice) with a textbox (txtReferenceCode) hosted in a TabControl (myTabControl) on MainWindow. From the UserControl I call a window (SearchWindow) which contains a list of stock items. The window needs to return a string value to the textbox contained by the UserControl. I cannot access the textbox on the UserControl from the window and thus cannot pass the string value from the window to the text property.
The UserControl is an instance loaded as a new tabItem (there may be many open as content of tabitems.) I need to only affect the current tabitem instance of the UserControl.
Eg: (Button Click Event in SearchWindow)
Invoice.txtReferenceCode.Text = SearchWindow.txtReferenceCode.Text
I need a simple uncomplicated, solution preferably in VB (but I'll take C# gladly).
I got it! I am posting the solution here for any who struggle with this issue.
XAML
WPF UserControl
<UserControl x:Class="Invoice"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<TextBox x:Name="txtReferenceCode" Width=100 />
</UserControl>
WPF Window
<Window x:Class="SearchWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<TextBox X:Name="TextToChangeTextBox" Width=100 />
</Window>
Code Behind
Add a Property to your Window
Class SearchWindow
Public ReadOnly Property TextValue
Get
Return TextToChangeTextBox.Text
End Get
End Property
...
End Class
now you can use the property in your window to pass a string to the TextBox on the UserControl.
Public Class Invoice
Private Sub SetValueToTextBox
Dim win As New SearchWindow
win.ShowDialog()
txtReferenceCode.Text = win.TextValue
End Sub
...
End Class
*
And That's it! EASY!
*
There are much better ways to go about doing this (i.e. share a viewmodel between the two windows and let binding update the textbox as needed).
But if you insist on doing it this way, try adding a public modifier to your textbox, that should let you access it like you want to.
<TextBox Name="txtReferenceCode" x:FieldModifier="public"/>

WPF How to expose fields on a UserControl

I have a simple application with just a window and a user control. The user control has a list box. The user control is positioned on the Window and I want to bind the user control's listbox to an element on the window's data context.
The examples I've been able to find have CLR properties on the user control which are accessed in code not via XAML.
<Window x:Class="WpfApplication2b.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication2b="clr-namespace:WpfApplication2b" Title="MainWindow" Height="410" Width="520">
<Grid>
<WpfApplication2b:MyUserControl></WpfApplication2b:MyUserControl>
</Grid>
And here is the user control itself.
<UserControl x:Class="WpfApplication2b.MyUserControl"
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 Background="#FFD8AA13">
<ListBox Height="276" HorizontalAlignment="Left" Margin="12,12,0,0" Name="listBox1" VerticalAlignment="Top" Width="276" />
</Grid>
As you can see it's just a listbox on a different coloured background. I have no idea where to go next :)
I'm guessing that I need to add a code behind property for the list box as a dependency property?
Edit: I've added a dependencyProperty, but I don't think I've quite got the point.
public partial class MyUserControl : UserControl
{
public static readonly DependencyProperty ListBoxProperty;
static MyUserControl()
{
FrameworkPropertyMetadata md = new FrameworkPropertyMetadata();
MyUserControl.ListBoxProperty = DependencyProperty.Register("MyListBox", typeof (ListBox),
typeof (MyUserControl), md);
}
public ListBox MyListBox
{
get
{
return (ListBox) GetValue(ListBoxProperty);
}
set
{
SetValue(ListBoxProperty, value);
}
}
public MyUserControl()
{
InitializeComponent();
}
}
Your UserControl will inherit the DataContext from the Window so you can bind properties on the ListBox as though it were declared in the Window. To make the control more flexible you can declare Dependency Properties for the data items from the DataContext that you want to use (i.e. an ItemsSource collection) and pass them into the control, rather than passing the ListBox out.
I think this question/answer is almost what you're looking for. Essentially you're going to need to make a dependency property (using the AddOwner registration method) and set up the DataBinding on the ListBox's ItemsSource to hook to the Dependency Property. The example in the answer does the same thing for a ComboBox, and should be almost the same for a ListBox.
Exposing inner Control properties for binding in WPF

Create a custom click event handler for a WPF usercontrol which contains a button?

have you ever found a problem when assigning a click event handler for your custom WPF usercontrol with a nested button control? I do.
When you put such user control in a main window, let's say Main.xaml, the MouseLeftButtonDown doesn't work, but the PreviewMouseLeftButtonDown works like a charm.
But imagine yourself telling each developer in your team to use this event when using your usercontrol... Some usercontrols in you library has MouseLeftButtonDown, others PreviewMouseLeftButtonDown.... It's a mess don't you agree?
So I've got a solution but I want someone to see if there's some elegant way to create your custom event handler called "Click".
In my usercontrol called CustomButton.xaml.cs, I have so far:
public partial class CustomButton: UserControl
{
public CustomButton()
: base()
{
this.InitializeComponent();
}
public delegate void ClickHandler(object sender, EventArgs e);
public event EventHandler Click;
public void Button_Click(object sender, RoutedEventArgs e) {//execute daddy's button click
(((sender as Button).Parent as Grid).Parent as CustomButton).Click(sender, e);
e.Handled = false;
}
In my CustomButton.xaml
<UserControl
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"
mc:Ignorable="d"
x:Class="YourCompany.UI.Controls.CustomButton" d:DesignHeight="72.5" d:DesignWidth="200">
<UserControl.Resources>
blablabla
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Button Style="{DynamicResource CustomButton}"
Width="{Binding ElementName=CustomButton, Path=ActualWidth}"
Cursor="Hand" Foreground="#ffffff" FontSize="28" Margin="8,8,0,12"
HorizontalAlignment="Left"
Content="Custom Button" Click="Button_Click" />
</Grid>
Now in my Main.xaml, the caller, I have:
<Window x:Class="YourCompany.MyProject.Main"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyProject!" Height="600" Width="800"
MinWidth="800" MinHeight="600" WindowState="Maximized" WindowStartupLocation="CenterScreen"
xmlns:bigbola="clr-namespace:YourCompany.UI.Controls;assembly=YourCompany.UI.Controls">
<mycontrols:CustomButton Name="test" MyImage=".\Images\btnOptions.png" Cursor="Hand"
Texto="See options" Click="test_Click"
Margin="168.367,176.702,253.609,0" ToolTip="See all options" Height="76.682"
VerticalAlignment="Top"></mycontrols:CustomButton>
Explanation:
in the usercontrol, when you click the nested button, it executes its parent custom "Click" handler.
Is there a elegant way to accomplish the same effect?
Going off of what mdm20 was saying... Why are you creating a UserControl (a collection of controls grouped into 1) when you could much more easily create a CustomControl (a control that extends the functionality of an existing control, such as a Button)? Assuming a Button is the only control you'd like in CustomButton, I'd highly recommend a CustomControl over what you have (a UserControl).
Example of UserControl vs CustomControl here
Hope this helps!
If your implementing a button, why not just derive from button?
To answer your question though, all you need it this.
if (Click != null) Click(this, EventArgs.Empty);
Couldn't this line:
(((sender as Button).Parent as Grid).Parent as CustomButton).Click(sender, e);
be replaced by
this.Click(sender, e);
?
Other than that though the answer depends on the exact behaviour that you want. If you want to click event of your user control to only trigger when you click on the inner button then I think you are handling it the right way. On the other hand if you want the click event to trigger whenever you click anywhere within the bounds of the user control then you are probably best styling or inheriting from the standard button control. Remember that in WPF the button's content can be any other element including another button.

Resources