Create a visual form of a control template in code

This is the next question to a previous question that really didn’t deliver me: deterministic and asynchronous field validation in WPF

Since WPF does not support INotifyDataErrorInfo , it seems to me that I need to implement something like this (please correct me if I am not here). I need this because I want the ViewModel to start when it displays special ErrorTemplates for certain fields (for example, after clicking a button or after a long async check operation or when the internal state changes so that certain fields suddenly become invalid).

I am thinking of writing my own markup extension or behavior for this. It listens to my version of INotifyDataErrorInfo implemented by ViewModel and creates a VisualTree from a special well-known ErrorTemplate defined in XAML after the ErrorsChanged event ErrorsChanged been raised.

Once I defined this template in XAML, how can I find it from my behavior / expression, materialize the actual visual tree from it, and then show it (maybe somehow at the adorner level) in the right position of the field on my form?

+6
source share
2 answers

You do not need a markup extension. I recently discovered that I wanted the same behavior, so I created a solution that works for my needs. Hope this also helps you.

The IDataErrorInfo interface actually contains everything needed to perform asynchronous signaling. What he lacks is an event system for automatically triggering notifications. There is a connection between this interface and the INotifyPropertyChanged interface. The combination of the two actually allows you to signal a change, somewhat indirectly.

First control:

 <TextBox Grid.Column="1" Width="100" Text="{Binding UpdateSourceTrigger=LostFocus, Path=Id, ValidatesOnDataErrors=true}" /> 

Pretty simple. The value of UpdateSourceTrigger does not matter, and NotifyOnValidationError not required, but nothing will hurt if you add it.

Further, a presentation model, which is just a far-fetched example. The important part is in the IDataErrorInfo index.

 public class WindowViewModel : INotifyPropertyChanged, IDataErrorInfo { public event PropertyChangedEventHandler PropertyChanged; private int _id; public int Id { get{ return _id; } set { _id = value; this.PropertyChanged( this, new PropertyChangedEventArgs( "Id" ) ); } } public string this[ string columnName ] { get { object result = AsynchValidationCoordinator.GetError( columnName ); if ( result != null ) { return result.ToString(); } return null; } } 

AsynchValidationCoordinator is a class that tracks properties and any related error information. For illustrative purposes, the key used is simply the name of the property, but you can easily create a compound key to differentiate potential property conflicts in scenarios with multiple view models.

 public static class AsynchValidationCoordinator { private static readonly ConcurrentDictionary<string, object> ErrorList = new ConcurrentDictionary<string, object>(); public static void CancelError( string propertyName, object error ) { object value; ErrorList.TryRemove( propertyName, out value ); } public static object GetError( string propertyName ) { object error = null; if ( ErrorList.ContainsKey( propertyName ) ) { ErrorList.TryRemove( propertyName, out error ); } return error; } public static void RegisterError( string propertyName, object error ) { ErrorList[propertyName] = error; } } 

Names of tracking properties are necessary, but you can create a completely different way to track them, including tracking names in the view model. It was an easy way for me to quickly apply a structured form to an existing project.

To tie this all together, I added the following ICommand property to the test view model and bound it to Button. ( RelayCommand by Josh Smith MSDN MVVM article .)

 public ICommand ValidateCommand { get { return new RelayCommand( Validate ); } } private void Validate( object value ) { Thread thread = new Thread( RaiseChanged ); thread.Start(); } private void RaiseChanged() { Thread.Sleep( 3000 ); AsynchValidationCoordinator.RegisterError( "Id", "Error Message Goes Here" ); this.PropertyChanged( this, new PropertyChangedEventArgs( "Id" ) ); } 

The source of the call does not matter. An important point that ties it all together is the fact that as soon as the PropertyChanged is called, the IDataErrorInfo indexer follows its tracks. Returning the error information registered in the AsynchValidationCoordinator causes the control's Validation.ErrorTemplate with the corresponding error message.

+6
source

INotifyDataErrorInfo now included in WPF 4.5 , as well as many other features. See the following links.

Here is a link to preview Visual Studio 11:
http://msdn.microsoft.com/en-us/vstudio/hh127353

0
source

Source: https://habr.com/ru/post/896472/


All Articles