2013-11-03 23:53:49 +13:00
/ *
* Greenshot - a free and open source screenshot tool
2016-03-14 05:07:53 +13:00
* Copyright ( C ) 2007 - 2015 Thomas Braun , Jens Klingen , Robin Krom
2013-11-03 23:53:49 +13:00
*
* For more information see : http : //getgreenshot.org/
* The Greenshot project is hosted on Sourceforge : http : //sourceforge.net/projects/greenshot/
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 1 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
* /
using System ;
using System.ComponentModel ;
using System.Reflection ;
namespace Greenshot.Drawing.Fields.Binding
{
/// <summary>
/// Bidirectional binding of properties of two INotifyPropertyChanged instances.
/// This implementation synchronizes null values, too. If you do not want this
/// behavior (e.g. when binding to a
/// </summary>
public class BidirectionalBinding
{
2016-05-23 03:17:02 +12:00
private readonly INotifyPropertyChanged _controlObject ;
private readonly INotifyPropertyChanged _fieldObject ;
private readonly string _controlPropertyName ;
private readonly string _fieldPropertyName ;
private bool _updatingControl = false ;
private bool _updatingField = false ;
private IBindingConverter _converter ;
private readonly IBindingValidator _validator ;
2013-11-03 23:53:49 +13:00
/// <summary>
/// Whether or not null values are passed on to the other object.
/// </summary>
protected bool AllowSynchronizeNull = true ;
/// <summary>
/// Bind properties of two objects bidirectionally
/// </summary>
2016-05-23 03:17:02 +12:00
/// <param name="controlObject">Object containing 1st property to bind</param>
/// <param name="controlPropertyName">Property of 1st object to bind</param>
/// <param name="fieldObject">Object containing 2nd property to bind</param>
/// <param name="fieldPropertyName">Property of 2nd object to bind</param>
2013-11-03 23:53:49 +13:00
public BidirectionalBinding ( INotifyPropertyChanged controlObject , string controlPropertyName , INotifyPropertyChanged fieldObject , string fieldPropertyName )
{
2016-05-23 03:17:02 +12:00
_controlObject = controlObject ;
_fieldObject = fieldObject ;
_controlPropertyName = controlPropertyName ;
_fieldPropertyName = fieldPropertyName ;
2013-11-03 23:53:49 +13:00
2016-05-23 03:17:02 +12:00
_controlObject . PropertyChanged + = ControlPropertyChanged ;
_fieldObject . PropertyChanged + = FieldPropertyChanged ;
2013-11-03 23:53:49 +13:00
}
/// <summary>
/// Bind properties of two objects bidirectionally, converting the values using a converter
/// </summary>
/// <param name="controlObject">Object containing 1st property to bind</param>
/// <param name="controlPropertyName">Property of 1st object to bind</param>
/// <param name="fieldObject">Object containing 2nd property to bind</param>
/// <param name="fieldPropertyName">Property of 2nd object to bind</param>
2016-03-14 05:07:53 +13:00
/// <param name="converter">taking care of converting the synchronized value to the correct target format and back</param>
public BidirectionalBinding ( INotifyPropertyChanged controlObject , string controlPropertyName , INotifyPropertyChanged fieldObject , string fieldPropertyName , IBindingConverter converter ) : this ( controlObject , controlPropertyName , fieldObject , fieldPropertyName )
2013-11-03 23:53:49 +13:00
{
2016-05-23 03:17:02 +12:00
_converter = converter ;
2013-11-03 23:53:49 +13:00
}
/// <summary>
/// Bind properties of two objects bidirectionally, converting the values using a converter.
/// Synchronization can be intercepted by adding a validator.
/// </summary>
/// <param name="controlObject">Object containing 1st property to bind</param>
/// <param name="controlPropertyName">Property of 1st object to bind</param>
/// <param name="fieldObject">Object containing 2nd property to bind</param>
/// <param name="fieldPropertyName">Property of 2nd object to bind</param>
2016-03-14 05:07:53 +13:00
/// <param name="validator">validator to intercept synchronization if the value does not match certain criteria</param>
public BidirectionalBinding ( INotifyPropertyChanged controlObject , string controlPropertyName , INotifyPropertyChanged fieldObject , string fieldPropertyName , IBindingValidator validator ) : this ( controlObject , controlPropertyName , fieldObject , fieldPropertyName )
2013-11-03 23:53:49 +13:00
{
2016-05-23 03:17:02 +12:00
_validator = validator ;
2013-11-03 23:53:49 +13:00
}
/// <summary>
/// Bind properties of two objects bidirectionally, converting the values using a converter.
/// Synchronization can be intercepted by adding a validator.
/// </summary>
/// <param name="controlObject">Object containing 1st property to bind</param>
/// <param name="controlPropertyName">Property of 1st object to bind</param>
/// <param name="fieldObject">Object containing 2nd property to bind</param>
/// <param name="fieldPropertyName">Property of 2nd object to bind</param>
2016-03-14 05:07:53 +13:00
/// <param name="converter">taking care of converting the synchronized value to the correct target format and back</param>
/// <param name="validator">validator to intercept synchronization if the value does not match certain criteria</param>
public BidirectionalBinding ( INotifyPropertyChanged controlObject , string controlPropertyName , INotifyPropertyChanged fieldObject , string fieldPropertyName , IBindingConverter converter , IBindingValidator validator ) : this ( controlObject , controlPropertyName , fieldObject , fieldPropertyName , converter )
2013-11-03 23:53:49 +13:00
{
2016-05-23 03:17:02 +12:00
_validator = validator ;
2013-11-03 23:53:49 +13:00
}
public void ControlPropertyChanged ( object sender , PropertyChangedEventArgs e )
{
2016-05-23 03:17:02 +12:00
if ( ! _updatingControl & & e . PropertyName . Equals ( _controlPropertyName ) )
2013-11-03 23:53:49 +13:00
{
2016-05-23 03:17:02 +12:00
_updatingField = true ;
Synchronize ( _controlObject , _controlPropertyName , _fieldObject , _fieldPropertyName ) ;
_updatingField = false ;
2013-11-03 23:53:49 +13:00
}
}
public void FieldPropertyChanged ( object sender , PropertyChangedEventArgs e )
{
2016-05-23 03:17:02 +12:00
if ( ! _updatingField & & e . PropertyName . Equals ( _fieldPropertyName ) )
2013-11-03 23:53:49 +13:00
{
2016-05-23 03:17:02 +12:00
_updatingControl = true ;
Synchronize ( _fieldObject , _fieldPropertyName , _controlObject , _controlPropertyName ) ;
_updatingControl = false ;
2013-11-03 23:53:49 +13:00
}
}
2016-05-23 03:17:02 +12:00
private void Synchronize ( INotifyPropertyChanged sourceObject , string sourceProperty , INotifyPropertyChanged targetObject , string targetProperty )
2013-11-03 23:53:49 +13:00
{
2016-05-23 03:17:02 +12:00
PropertyInfo targetPropertyInfo = ResolvePropertyInfo ( targetObject , targetProperty ) ;
PropertyInfo sourcePropertyInfo = ResolvePropertyInfo ( sourceObject , sourceProperty ) ;
2013-11-03 23:53:49 +13:00
if ( sourcePropertyInfo ! = null & & targetPropertyInfo ! = null & & targetPropertyInfo . CanWrite )
{
object bValue = sourcePropertyInfo . GetValue ( sourceObject , null ) ;
2016-05-23 03:17:02 +12:00
if ( _converter ! = null & & bValue ! = null )
2013-11-03 23:53:49 +13:00
{
2016-05-23 03:17:02 +12:00
bValue = _converter . convert ( bValue ) ;
2013-11-03 23:53:49 +13:00
}
try
{
2016-05-23 03:17:02 +12:00
if ( _validator = = null | | _validator . validate ( bValue ) )
2013-11-03 23:53:49 +13:00
{
targetPropertyInfo . SetValue ( targetObject , bValue , null ) ;
}
}
catch ( Exception e )
{
2016-05-23 03:17:02 +12:00
throw new MemberAccessException ( "Could not set property '" + targetProperty + "' to '" + bValue + "' [" + ( bValue ! = null ? bValue . GetType ( ) . Name : "" ) + "] on " + targetObject + ". Probably other type than expected, IBindingCoverter to the rescue." , e ) ;
2013-11-03 23:53:49 +13:00
}
}
}
2016-05-23 03:17:02 +12:00
private static PropertyInfo ResolvePropertyInfo ( object obj , string property )
2013-11-03 23:53:49 +13:00
{
PropertyInfo ret = null ;
string [ ] properties = property . Split ( "." . ToCharArray ( ) ) ;
for ( int i = 0 ; i < properties . Length ; i + + )
{
string prop = properties [ i ] ;
ret = obj . GetType ( ) . GetProperty ( prop ) ;
if ( ret ! = null & & ret . CanRead & & i < prop . Length - 1 )
{
obj = ret . GetValue ( obj , null ) ;
}
}
return ret ;
}
public IBindingConverter Converter
{
2016-05-23 03:17:02 +12:00
get { return _converter ; }
set { _converter = value ; }
2013-11-03 23:53:49 +13:00
}
}
}