All the controls missing in WPF. Over 1 million downloads.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

341 lines
9.5 KiB

/*************************************************************************************
Toolkit for WPF
Copyright (C) 2007-2020 Xceed Software Inc.
This program is provided to you under the terms of the XCEED SOFTWARE, INC.
COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at
https://github.com/xceedsoftware/wpftoolkit/blob/master/license.md
For more features, controls, and fast professional support,
pick up the Plus Edition at https://xceed.com/xceed-toolkit-plus-for-wpf/
Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids
***********************************************************************************/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Data;
using System.Collections;
using Xceed.Wpf.Toolkit.Core.Utilities;
using System.Linq.Expressions;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Threading;
namespace Xceed.Wpf.Toolkit.PropertyGrid
{
[TemplatePart( Name = "content", Type = typeof( ContentControl ) )]
public class PropertyItem : CustomPropertyItem
{
#region Properties
#region IsReadOnly
/// <summary>
/// Identifies the IsReadOnly dependency property
/// </summary>
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register( "IsReadOnly", typeof( bool ), typeof( PropertyItem ), new UIPropertyMetadata( false, OnIsReadOnlyChanged ) );
public bool IsReadOnly
{
get { return ( bool )GetValue( IsReadOnlyProperty ); }
set { SetValue( IsReadOnlyProperty, value ); }
}
private static void OnIsReadOnlyChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
{
var propertyItem = o as PropertyItem;
if( propertyItem != null )
propertyItem.OnIsReadOnlyChanged( (bool)e.OldValue, (bool)e.NewValue );
}
protected virtual void OnIsReadOnlyChanged( bool oldValue, bool newValue )
{
if( this.IsLoaded )
{
this.RebuildEditor();
}
}
#endregion //IsReadOnly
#region IsInValid
/// <summary>
/// Identifies the IsInvalid dependency property
/// </summary>
public static readonly DependencyProperty IsInvalidProperty =
DependencyProperty.Register( "IsInvalid", typeof( bool ), typeof( PropertyItem ), new UIPropertyMetadata( false, OnIsInvalidChanged ) );
public bool IsInvalid
{
get
{
return ( bool )GetValue( IsInvalidProperty );
}
internal set
{
SetValue( IsInvalidProperty, value );
}
}
private static void OnIsInvalidChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
{
var propertyItem = o as PropertyItem;
if( propertyItem != null )
propertyItem.OnIsInvalidChanged( ( bool )e.OldValue, ( bool )e.NewValue );
}
protected virtual void OnIsInvalidChanged( bool oldValue, bool newValue )
{
var be = this.GetBindingExpression( PropertyItem.ValueProperty );
if( newValue )
{
var validationError = new ValidationError( new InvalidValueValidationRule(), be );
validationError.ErrorContent = "Value could not be converted.";
Validation.MarkInvalid( be, validationError );
}
else
{
Validation.ClearInvalid( be );
}
}
#endregion // IsInvalid
#region PropertyDescriptor
public PropertyDescriptor PropertyDescriptor
{
get;
internal set;
}
#endregion //PropertyDescriptor
#region PropertyName
public string PropertyName
{
get
{
return (this.DescriptorDefinition != null) ? this.DescriptorDefinition.PropertyName : null;
}
}
#endregion
#region PropertyType
public Type PropertyType
{
get
{
return ( PropertyDescriptor != null )
? PropertyDescriptor.PropertyType
: null;
}
}
#endregion //PropertyType
#region DescriptorDefinition
internal DescriptorPropertyDefinitionBase DescriptorDefinition
{
get;
private set;
}
#endregion DescriptorDefinition
#region Instance
public object Instance
{
get;
internal set;
}
#endregion //Instance
#endregion //Properties
#region Overrides
protected override string GetPropertyItemName()
{
return this.PropertyName;
}
protected override Type GetPropertyItemType()
{
return this.PropertyType;
}
protected override void OnIsExpandedChanged( bool oldValue, bool newValue )
{
if( newValue && this.IsLoaded )
{
this.GenerateExpandedPropertyItems();
}
}
protected override object OnCoerceValueChanged( object baseValue )
{
// Propagate error from DescriptorPropertyDefinitionBase to PropertyItem.Value
// to see the red error rectangle in the propertyGrid.
BindingExpression be = this.GetBindingExpression( PropertyItem.ValueProperty );
this.SetRedInvalidBorder( be );
return baseValue;
}
protected override void OnValueChanged( object oldValue, object newValue )
{
base.OnValueChanged( oldValue, newValue );
// A Default Value is defined and newValue is null => set the Default Value
if( ( newValue == null ) && ( this.DescriptorDefinition != null ) && ( this.DescriptorDefinition.DefaultValue != null ) )
{
#if VS2008
this.Value = this.DescriptorDefinition.DefaultValue;
#else
this.SetCurrentValue( PropertyItem.ValueProperty, this.DescriptorDefinition.DefaultValue );
#endif
}
}
#endregion
#region Internal Methods
internal void SetRedInvalidBorder( BindingExpression be )
{
if( (be != null) && be.DataItem is DescriptorPropertyDefinitionBase )
{
DescriptorPropertyDefinitionBase descriptor = be.DataItem as DescriptorPropertyDefinitionBase;
if( Validation.GetHasError( descriptor ) )
{
this.Dispatcher.BeginInvoke( DispatcherPriority.Input, new Action( () =>
{
var errors = Validation.GetErrors( descriptor );
Validation.MarkInvalid( be, errors[ 0 ] );
}
) );
}
}
}
internal void RebuildEditor()
{
var objectContainerHelperBase = this.ContainerHelper as ObjectContainerHelperBase;
//Re-build the editor to update this propertyItem
var editor = objectContainerHelperBase.GenerateChildrenEditorElement( this );
if( editor != null )
{
// Tag the editor as generated to know if we should clear it.
ContainerHelperBase.SetIsGenerated( editor, true );
this.Editor = editor;
//Update Source of binding and Validation of PropertyItem to update
var be = this.GetBindingExpression( PropertyItem.ValueProperty );
if( be != null )
{
be.UpdateSource();
this.SetRedInvalidBorder( be );
}
}
}
#endregion
#region Private Methods
private void OnDefinitionContainerHelperInvalidated( object sender, EventArgs e )
{
if( this.ContainerHelper != null )
{
this.ContainerHelper.ClearHelper();
}
var helper = this.DescriptorDefinition.CreateContainerHelper( this );
this.ContainerHelper = helper;
if( this.IsExpanded )
{
helper.GenerateProperties();
}
}
private void Init( DescriptorPropertyDefinitionBase definition )
{
if( definition == null )
throw new ArgumentNullException( "definition" );
if( this.ContainerHelper != null )
{
this.ContainerHelper.ClearHelper();
}
this.DescriptorDefinition = definition;
this.ContainerHelper = definition.CreateContainerHelper( this );
definition.ContainerHelperInvalidated += new EventHandler( OnDefinitionContainerHelperInvalidated );
this.Loaded += this.PropertyItem_Loaded;
}
private void GenerateExpandedPropertyItems()
{
if( this.IsExpanded )
{
// This withholds the generation of all PropertyItem instances (recursively)
// until the PropertyItem is expanded.
var objectContainerHelper = ContainerHelper as ObjectContainerHelperBase;
if( objectContainerHelper != null )
{
objectContainerHelper.GenerateProperties();
}
}
}
#endregion
#region Event Handlers
private void PropertyItem_Loaded( object sender, RoutedEventArgs e )
{
this.GenerateExpandedPropertyItems();
}
#endregion
#region Constructors
internal PropertyItem( DescriptorPropertyDefinitionBase definition )
: base( definition.IsPropertyGridCategorized, !definition.PropertyType.IsArray )
{
this.Init( definition );
}
#endregion //Constructors
private class InvalidValueValidationRule : ValidationRule
{
public override ValidationResult Validate( object value, CultureInfo cultureInfo )
{
// Will always return an error.
return new ValidationResult( false, null );
}
}
}
}