Introduction
The MVVM is an architectural pattern from Microsoft. View Class has no idea about model exist however viewmodel and model don’t know about view. It is a loosely coupled design.
This separates the role of designer to focus on UI instead of programming/business logic and allows application to work in different work streams.
If interested, get more details on 5 Minute Overview of MVVM in Silverlight.
This article explains the problem of how to set focus inside textbox
(View) when we use validation logic when using MVVM pattern.
Background
One of my friends had given me a problem and asked me for a solution which I feel is required by most developers, so I decided to write an article on this which might be helpful.
As it is a general scenario that an application has some control (Textbox
) which needs input from the end user and when end user performs some action (Save or move to another form), we do validation on button click. Now in Silverlight, with the help of ValidatesOnDataErrors=True attribute display UI with red border outside textbox
which indicates required field/invalid data. You can see in the below Screen 1 and Screen 2.
However, it would be good if it will blink cursor inside the textbox
so that user does not need to explicitly click mouse to set focus inside textbox
after which only he/she can enter input.
So the point of interest here is to achieve this using MVVM model without writing any code in View code-behind file.
The below screen 3 explains more:
Using the Code
Currently Silverlight 4.0 doesn’t provide directly CommandManager
, with the help of how to Creating a command manager in Silverlight as this command manager will be implemented in View which will bind the command of button with click event.
<Button Content="Click!"="3"Grid.Column="2"Grid.ColumnSpan="2"
Cmd:CommandManager.CommandEventName="Click"
Cmd:CommandManager.Command="{Binding ClickCommand }" />
As currently this CommandManager
is created as a separated library which can be used across any other Silverlight class library.
Class TextBoxAttach
Now define a class name as TextBoxAttach which has Dependency
property (TextBoxControllerProperty). This property registers a function OnTextBoxControllerChanged which will execute whenever the textbox
control loads. The important point in the below code is when this property has been attached to textbox
, then it has one dictionary being defined called elements1
which stores the textbox
name as ID
.
private static readonly Dictionary<string,TextBox> elements1 =
new Dictionary<string,TextBox>();
private static void OnTextBoxControllerChanged
(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = d as TextBox;
if (element == null)
throw new ArgumentNullException("d");
var newController = e.NewValue as ITextBoxController;
if (newController != null)
{
elements1.Add(element.Name, element);
newController.ClickButton += ClickButton;
}
}
Now when you click on Button, the ClickButton event fires which checks if textbox
is empty to those textbox
es which have been attached with TextBoxControllerProperty and it will set the focus inside textbox
.
private static void ClickButton(ITextBoxController sender)
{
foreach (KeyValuePair<string,TextBox> pair in elements1)
{
TextBox element;
string key = pair.Key;
element = pair.Value;
if (string.IsNullOrEmpty(element.Text))
{
element.Focus();
break;
}
}
}
So to use this TextBoxAttach
to set focus inside a textbox
, the XAML code will be something like this:
<TextBox x:Name="textBox1" loc:TextBoxAttach.TextBoxController="{Binding}" />
Class MyViewModel
Class MyViewModel inherits from ITextBoxController and uses event ClickEventhandler which is defined in ITextBoxController. And here in the constructor of MyViewModel initialize the ClickCommand with the help of RelayCommand which itself inherits from ICommand.
public MyViewModel()
{
Value1 = "My Text1";
Value2 = "My Text2";
ClickCommand = new RelayCommand(p =>
{
if (ClickButton != null)
ClickButton(this);
});
}
Now to display validation error UI to view MyViewModel class also inherits from IDataErrorInfo which implements the indexer also known as smart arrays in C#. It is pretty much like defining properties:
public string this[string columnName]
{
get
{
string strMessage = string.Empty;
CustomValidation(ref strMessage, columnName);
return strMessage;
}
}
And to use this validation feature, the XAML code will be like this:
<TextBox x:Name="textBox1" Text="{Binding Path=Value1,
Mode=TwoWay,ValidatesOnDataErrors=True}" />
Points of Interest
- MVVM- How can I select text in a textbox?
- Model-View-ViewModel In Silverlight Apps.
Finally
As there is always scope for improvement, it would be good if you have any comments/complaints/suggestions, kindly let me know. I will try to address those points and your feedback will help me to improve my future articles on CodeProject J.
History