Is there a ProcessTabKey equivalent in Silverlight (focus on next control in hierarchy) - silverlight-4.0

Is there an equivalent method to WinForm's ProcessTabKey in Silverlight, or a way of correctly simulating it?
Looking around what I see are people are hard coding every single control (text1 has a KeyDown event to focus text2, text2 has a KeyDown event to focus text3, etc). A few have progressed to querying part of the layout hierarchy based on TabIndex, but by default all controls have the same TabIndex so it is once again a manual setup task.
A final wrinkle is dealing with nested containers. For example a vertically oriented stackpanel of textboxes, followed by a horizontally oriented stackpanel of buttons. Most programmatic approaches I've seen try to assume all controls are direct children of the same parent container.
I understand that Silverlight must operate under some restrictions, to prevent a Silverlight application from tabbing focus back to part of the browser (a potential security risk), but I hope there is some way to create a proper Enter-to-Tab setup without hand crafting all forms.

There is a related question in StackOverflow: Auto-tab in Silverlight 3
I'm using a custom attached Behavior (System.Windows.Interactivity) to avoid code behind because Iand works well with MVVM.
<UserControl x:Class="SCO.Ria.UI.Views.LoginView"
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:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:Caliburn.Micro.Focus;assembly=Caliburn.Micro.Focus" mc:Ignorable="d"
d:DesignHeight="154" d:DesignWidth="468">
<interactivity:Interaction.Behaviors>
<local:TabNextBehavior/>
</interactivity:Interaction.Behaviors>
</UserControl>
TabNextBehavior.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace Caliburn.Micro.Focus {
public class TabNextBehavior : Behavior<Control> {
protected override void OnAttached() {
base.OnAttached();
this.AssociatedObject.KeyUp += AssociatedObject_KeyUp;
}
protected override void OnDetaching() {
base.OnDetaching();
this.AssociatedObject.KeyUp -= AssociatedObject_KeyUp;
}
void AssociatedObject_KeyUp(object sender, KeyEventArgs args) {
if (args.Key == System.Windows.Input.Key.Enter) {
DependencyObject parent = null;
if (AssociatedObject is ChildWindow)
parent = ((ChildWindow)parent).Content as DependencyObject;
else parent = AssociatedObject;
parent.TabNext(); //extensin Method from VisualTreeExtensions.cs
}
}
}
You can see a gist here: https://gist.github.com/4576803

Related

Issues with ContentDialog and x:Bind

I have a ContentDialog that implements INotifyPropertyChanged. I have a TextBox with its Text property bound via x:Bind syntax to a string property on my code-behind.
If I edit the contents of the TextBox, and then click on another control in the ContentDialog (losing focus on the TextBox), and then click on the primary button, by the time I get to the primary button click event handler the text property has been updated with the content of the TextBox control = PERFECT.
However, if I change the contents of the TextBox but then keep it in focus and THEN click on the primary button the binding never gets updated.
It seems that the reason for this is because the built-in buttons in ContentDialog do not acquire focus before their click eventhandlers are fired, and as x:Bind only supports LostFocus binding, these bindings will never be updated.
I'm staggered that this big bug exists. So my 2 questions are
1) Is there a workaround
2) Do I have to abandon x:Bind and use WPF-style Binding syntax, where I can change the UpdateSourceTrigger within the binding itself.
I'm hoping that another UWP dev has come across this and knows of a work around
EDIT
Ive created some example code to demonstrate the issue.
page:
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Open" Click="OpenClick" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>
code behind:
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
}
private async void OpenClick(object sender, RoutedEventArgs e)
{
var dialog = new ContentDialog1();
await dialog.ShowAsync();
}
}
content dialog:
<ContentDialog
x:Class="App1.ContentDialog1"
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"
Title="TITLE"
PrimaryButtonText="Button1"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick">
<Grid>
<TextBox Text="{x:Bind Path=TestText, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="100"/>
</Grid>
</ContentDialog>
content dialog code behind:
public sealed partial class ContentDialog1 : ContentDialog, INotifyPropertyChanged
{
public ContentDialog1()
{
InitializeComponent();
}
private string _testText;
public string TestText
{
get => _testText;
set
{
_testText = value;
OnPropertyChanged(nameof(TestText));
}
}
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And my target sdk verions are:
If you click open the content dialog, enter some text, then click the primary button - if you have a breakpoint in the click handler you'll see that the binding hasn't updated with your entered text
OK, I've looked into this, and have tested this on all SDK's back to build 10586. Essentially ContentDialog is broken for x:Bind bindings to TextBox, because TextBox is required to lose focus in order for its bindings to update.
I have tried a number of hacks including walking the visual tree on Loaded to add extra handlers to the hidden buttons, and trying to force them to focus (thus removing focus from any TextBox, and triggering the binding update) before the click handler fires, but without UWP supporting the tunnelling event strategy (like WPF), it is not possible to inject any kind of event handling between the button press and the hard-wired click handler running.
The upshot is that x:Bind (which doesn't support UpdateSourceTrigger other than LostFocus) cannot reliably work in ContentDialog with TextBox controls.
I have logged a bug at Microsoft uservoice.
The only way to solve this is to abandon x:Bind and use classic Bindings which support an UpdateSourceTrigger of PropertyChanged - thus allowing bindings to be always up-to-date before the button in clicked.
But even this is a hack, because UpdateSourceTrigger of PropertyChanged will cause unnecessary updates in your setters - which can be problematic. Also updating setters in INotifyPropertyChanged will fire many additional rounds of the arrange/measure pass for all of your layout.
The only real solution is to write your own ContentDialog control. I was tempted to do this, but without access to Microsofts source code for these controls, it's difficult to know if you have done it successfully
I can't reproduce the issue: the setter of string property in code-behind is always fired before the ContentDialog_PrimaryButtonClick, even if I click the primary button while the focus is still on the TextBox. My sample (tested on Fall Creators Update):
MyContentDialog.xaml
<Grid>
<TextBox Text="{x:Bind MyString, Mode=TwoWay}"/>
</Grid>
MyContentDialog.xaml.cs
public sealed partial class MyContentDialog : ContentDialog, INotifyPropertyChanged
{
private DTO dto;
private string myString;
public string MyString
{
get
{
return myString;
}
set
{
myString = value;
NotifyPropertyChanged(nameof(MyString));
}
}
public MyContentDialog(DTO dto)
{
this.InitializeComponent();
this.dto = dto;
MyString = dto.text;
}
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
dto.text = MyString;
}
private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
DTO.cs
public class DTO
{
public string text;
}
MainPage.xaml.cs
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
DTO dto = new DTO { text = "My text" };
MyContentDialog dialog = new MyContentDialog(dto);
await dialog.ShowAsync();
await new MessageDialog("My text, after being edited by the user in MyContentDialog: " + dto.text).ShowAsync();
}
Could you share your code? What's the min and target version of your project?
I just figured it out I spent a lot of time just like you did almost two days failed and Content Dialog crashes my application if there is null object binding to any of the control have to use Converters.
I was just thinking how about placing a usercontrol on the dialog and see if the binding works.
Let me try post the results here.
If you have figured out any other way of doing this let me know

Prism MVVM Binding to User Control Dependency Property

I have a Universal app that I'm writing and I want to use Prism and Unity as my MVVM framework. Everything was going great until I got to a view where I have multiple instances of the same user control(a custom Watermark Textbox). For some reason, I haven't been able to find a good solution to my problem. I imagine I'm overlooking something and there is a straightforward answer.
Here's my source code(just the relevant portions). Some background, I had this working before implementing Prism. The Commands attached to the user controls are firing as expected but I can't figure out how to manipulate the control itself):
Any guidance on how to use user controls with Prism or binding to dependency properties with Prism would be great. Thanks.
My View
<!-- I want to be able to set the watermark and also retrieve the text from my ViewModel -->
<uc:WatermarkTextBox Width="250"
x:Name="FullName">
<i:Interaction.Behaviors>
<iCore:EventTriggerBehavior EventName="GotFocus">
<iCore:InvokeCommandAction Command="{Binding EntryFieldFocus}"
CommandParameter="{Binding ElementName=FullName}"/>
</iCore:EventTriggerBehavior>
</i:Interaction.Behaviors>
</uc:WatermarkTextBox>
<!-- This one as well -->
<uc:WatermarkTextBox Width="250"
x:Name="EmailAddress">
<i:Interaction.Behaviors>
<iCore:EventTriggerBehavior EventName="GotFocus">
<iCore:InvokeCommandAction Command="{Binding EntryFieldFocus}"
CommandParameter="{Binding ElementName=EmailAddress}" />
</iCore:EventTriggerBehavior>
</i:Interaction.Behaviors>
</uc:WatermarkTextBox>
Going from your comments/error message ("Failed to assign to property %0") when attempting to bind the property directly, the Property you are trying to bind to isn't a dependency property.
Your WatermarkTextBox only implements a CLR Property, like this:
public class WatermarkTextBox : TextBox {
public object Watermark { get; set; }
}
This can't be used for databinding.
You need to implement it as Dependency Property, like
public class WatermarkTextBox : TextBox {
public object Watermark
{
get { return (object)GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
}
public static readonly DependencyProperty WatermarkProperty =
DependencyProperty.Register("Watermark", typeof(object), typeof(WatermarkTextbox), new PropertyMetadata(null));
}
This will allow to use <uc:WatermarkTextBox Watermark={Binding watermarkText, Mode=TwoWay} for example.
If you can't modify WatermarkTextBox (third-party closed source control), then you'll have to implement an attached behavior and then bind your ViewModel Property to the attached behavior and assign the Watermark property from within it.
If you need TwoWay binding (which is unlikely in case of Watermark property, since it won't be changed from UI), you have to register an event handler (KeyDown in linked answer) to this specific event and pass the new value to the attached property
Except these two ways, there is no way to update a Control's CLR property without violating MVVM.

What are people using instead of IMultiValueConverters in Windows 8?

I'm porting an existing WPF application to a Windows 8 application.
In the WPF application we make considerable use of MultiValue convertors to allow us to create values that are combinations of UI element properties and viewmodel properties (the weight of a hippo object and the actualWidth of an itemscontrol) to achieve nice UI effects.
Windows 8, however, doesn't have a MultiValue convertor.
Since I'm porting an application I don't really want to significantly change my viewmodels or Xaml.
How can I replicate Multivalue controller functionality with a minimum amount of pain and rewriting?
My approach was to expose a static singleton instance of the VM; for example:
public class MainViewModel : INotifyPropertyChanged
{
private static MainViewModel _mvm;
public static MainViewModel MVM
{
get
{
if (_mvm == null)
_mvm = new MainViewModel();
return _mvm;
}
}
Then simple pass the whole VM through:
<Button Command="{Binding MyCommand}" CommandParameter="{Binding MVM}">Run Command</Button>
It's not multibinding, but it does allow you to pass multiple parameters.

Is there something similar to TabControlRegionAdapter.ItemContainerStyle Attached Property for ItemsControl?

I'm using Prism 4 with Silverlight and I want to use ItemsControl to host multiple views. I really want all the views to be wrapped inside a specified ItemTemplate or be able to specify an ItemStyle so that I can use something like the Expander control in the Silverlight Toolkit . When I try to specify an ItemTemplate an unhandled System.NotSupportedException is thrown at runtime.
ItemsControl.Items must not be a UIElement type when an ItemTemplate is set.
at System.Windows.Controls.ItemsControl.MS.Internal.Controls.IGeneratorHost.GetContainerForItem(Object item, DependencyObject recycledContainer)
at System.Windows.Controls.ItemContainerGenerator.Generator.GenerateNext(Boolean stopAtRealized, Boolean& isNewlyRealized)
at System.Windows.Controls.ItemContainerGenerator.System.Windows.Controls.Primitives.IItemContainerGenerator.GenerateNext(Boolean& isNewlyRealized)
at System.Windows.Controls.ItemsControl.AddContainers()
at System.Windows.Controls.ItemsControl.RecreateVisualChildren(IntPtr unmanagedObj)
Code
<ItemsControl Regions:RegionManager.RegionName="DetailsRegion">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Red" BorderThickness="1">
<ContentPresenter Content="{Binding}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It has been a while since I used PRISM, but the following is an example that you can use to implement a custom IRegion that wraps the element before adding it to the collection.
public class RegionWrapper : Region
{
public override Microsoft.Practices.Composite.Regions.IRegionManager Add(object view, string viewName, bool createRegionManagerScope)
{
var myWrapper = new Wrapper();
myWrapper.Content = view;
return base.Add(myWrapper, viewName, createRegionManagerScope);
}
}
To register this item you need to create a Region factory, which in PRISM they call an adapter
public class RegionWrapperAdapter : RegionAdapterBase<IRegionAdapter>
{
protected override Microsoft.Practices.Composite.Regions.IRegion CreateRegion()
{
return new RegionWrapper();
}
}
Then on your Bootstrap just register your adapter
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
var regionAdapterMappings = base.ConfigureRegionAdapterMappings();
regionAdapterMappings.RegisterMapping(typeof(ItemsControl), Container.Resolve<RegionWrapperAdapter>());
return regionAdapterMappings;
}
Of course the other part left is to implement the control 'Wrapper' so you can create that class and add the content. It could simply be a ContentControl with a particular style similar to what you have in this example or add anything fancier.
This code is based on an old version of PRISM, so things might have changed recently.
Hope this helps
Miguel

Getting Unity to Resolve views in XAML

I'm starting out with MVVM, and I'm starting to understand things. I'm currently experimenting with the Cinch framework, though I'm not committed to it as of yet.
I was injecting the ViewModels into the Views using by having a reference to the ViewModel in the codebehind of the view, with the property having a [Dependency] on it, and in the setter it sets the DataContext to the right view, using Unity. Neat trick, I thought.
I'm trying to get my app to work as a single Window, with injected views (As opposed to multiple windows and dealing with opening\closing them)
I changed my views from Windows to UserControls, and added a to the main window.
That worked, but the ViewModel was never injected, presumably because the XAML doesn't use Container.Resolve to create the view, as when I created the view and added it manually in the code-behind using Resolve, the [Dependency] was created.
How can I set up my window, so that if I add a view through XAML, or the view gets changed as a result of a UI action etc, it gets it through Unity, so that it can work its magic?
This problem is normally solved using Regions and the RegionManager. In the main window ViewModel, a set of Regions is created and added to the RegionManager. Then ViewModels can be Resolved and added to the Region.Views collection.
In XAML, the Region is normally injected by having the ItemsSource property of an ItemsControl bound to the region property of the main ViewModel.
So, in the main screen ViewModel you would have something like this:
public class TestScreenViewModel
{
public const string MainRegionKey = "TestScreenViewModel.MainRegion";
public TestScreenViewModel(IUnityContainer container, IRegionManager regionManager)
{
this.MainRegion = new Region();
regionManager.Regions.Add(MainRegionKey, this.MainRegion);
}
public Region MainRegion { get; set; }
}
This would be Resolved normally in your IModule
#region IModule Members
public void Initialize()
{
RegisterViewsAndServices();
var vm = Container.Resolve<SelectorViewModel>();
var mainScreen = Container.Resolve<TestScreenViewModel>();
mainScreen.MainRegion.Add(vm);
var mainView = ContentManager.AddContentView("Test harness", mainScreen);
}
#endregion
And the XAML representation of your template looking something like
<DataTemplate DataType="{x:Type TestModule:TestScreenViewModel}">
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto">
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=MainRegion.Views}" />
</StackPanel>
</ScrollViewer>
</DataTemplate>
The way to solve your problem is to make your window to have a ViewModel as well, with ViewModels of UserControls exposes as properties on it. Then in your XAML for a window you'd simply use Binding mechanism to bind UserControl's DataContexts to proper properties of your your main ViewModel. And since that main ViewModel is resolved from Unity container it would have all other ViewModel-s injected as needed.