How to define value of dynamic resource based on another dynamic resource? - wpf

Is it possible to assign value to a dynamic resource from another dynamic resource?
For example
<sys:Double x:Key="ButtonWidth">48</sys:Double>
<sys:Double x:Key="SmallButtonWidth"> ButtonWidth / 2 </sys:Double>

It is possible to transform a value using a custom MarkupExtension.
e.g.
<Window.Resources xmlns:ms="clr-namespace:WpfApplication1.MathShit">
<sys:Double x:Key="ButtonWidth">48</sys:Double>
<ms:Calculation x:Key="SmallButtonWidth">
<ms:Product Operand1="{ms:Value {StaticResource ButtonWidth}}"
Operand2="0.5" />
</ms:Calculation>
</Window.Resources>
namespace WpfApplication1.MathShit
{
[ContentProperty("Expression")]
public class Calculation : MarkupExtension
{
public IExpression Expression { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (Expression == null) throw new Exception("Expression cannot be null.");
return Expression.CalculateValue();
}
}
[TypeConverter(typeof(ExpressionConverter))]
public interface IExpression
{
double CalculateValue();
}
public abstract class BinaryOperation : IExpression
{
public IExpression Operand1 { get; set; }
public IExpression Operand2 { get; set; }
public double CalculateValue()
{
if (Operand1 == null) throw new Exception("Operand1 cannot be null.");
if (Operand2 == null) throw new Exception("Operand2 cannot be null.");
return CalculateBinaryOperation();
}
protected abstract double CalculateBinaryOperation();
}
public class Sum : BinaryOperation
{
protected override double CalculateBinaryOperation()
{
return Operand1.CalculateValue() + Operand2.CalculateValue();
}
}
public class Product : BinaryOperation
{
protected override double CalculateBinaryOperation()
{
return Operand1.CalculateValue() * Operand2.CalculateValue();
}
}
public class Value : MarkupExtension, IExpression
{
public double? Double { get; set; }
public Value() { }
public Value(double #double)
: this()
{
this.Double = #double;
}
public double CalculateValue()
{
if (Double == null) throw new Exception("Double");
return Double.Value;
}
// Allows easy object instantiation in XAML attributes. (Result of StaticResource is not piped through ExpressionConverter.)
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
public class ExpressionConverter : DoubleConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
var doubleValue = (double)base.ConvertFrom(context, culture, value);
return (IExpression)new Value(doubleValue);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
var val = (Value)value;
return base.ConvertTo(context, culture, val.CalculateValue(), destinationType);
}
}
}
With this you can build arbitrary expression trees (which is a lot more easy than parsing math strings which you can do as well of course if you do not mind the trouble).

Related

WPF designer properties window: property does not show nested properties

I am trying to edit a complex property of a WPF custom UserControl in the properties windows. But, the nested properties of the property are not always expandable. In the verbose form (don't know the corrent name):
<local:PointControl>
<local:PointControl.Point>
<local:Point X="3" Y="4"/>
</local:PointControl.Point>
</local:PointControl>
I can edit the nested properties:
In the short form, provided by a TypeConverter:
<local:PointControl Point="0,0"/>
I cannot edit the nested properties:
The code is as follows:
Point
namespace WpfApplication1
{
[TypeConverter(typeof(PointConverter))]
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point() { }
public Point(int x, int y)
{
X = x;
Y = y;
}
public static bool CanParse(string value); // not important
public static Point Parse(string value); // not important
public override string ToString()
{
return string.Format("{0},{1}",X,Y);
}
}
}
PointConverter
namespace WpfApplication1
{
public class PointConverter : TypeConverter
{
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value is Point)
{
return (value as Point).ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); ;
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
var point = Point.Parse(value as string);
return point;
}
return base.ConvertFrom(context, culture, value);
}
public override bool IsValid(ITypeDescriptorContext context, object value)
{
if (!(value is string))
{
return false;
}
return Point.CanParse(value as string);
}
}
}
UserControl
namespace WpfApplication1
{
public partial class PointControl : UserControl
{
public Point Point
{
get;set;
}
public PointControl()
{
InitializeComponent();
}
}
}
UPDATE
When I add an inline value editor it won't update the XAML... :(.
xaml:
<local:PointControl Point="2,2">
MetaData.cs
[assembly: ProvideMetadata(typeof(WpfApplication1.Design.Metadata))]
namespace WpfApplication1.Design
{
internal class Metadata : IProvideAttributeTable
{
public AttributeTable AttributeTable
{
get
{
AttributeTableBuilder builder = new AttributeTableBuilder();
builder.AddCustomAttributes(typeof(WpfApplication1.PointControl), nameof(WpfApplication1.PointControl.Point), PropertyValueEditor.CreateEditorAttribute(typeof(PointerValueEditor)));
return builder.CreateTable();
}
}
}
}
PointerValueEditor
namespace WpfApplication1.Design
{
class PointerValueEditor : PropertyValueEditor
{
public PointerValueEditor()
{
var stringReader = new System.IO.StringReader(
#"<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
<StackPanel>
<TextBox Text=""{Binding Value.X, Mode=TwoWay, UpdateSourceTrigger=LostFocus}""/>
<TextBox Text=""{Binding Value.Y, Mode=TwoWay, UpdateSourceTrigger=LostFocus}""/>
</StackPanel>
</DataTemplate>");
var xmlReader = System.Xml.XmlReader.Create(stringReader);
var dataTemplate = System.Windows.Markup.XamlReader.Load(xmlReader) as DataTemplate;
this.InlineEditorTemplate = dataTemplate;
}
}
}
The Code is based on https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2013/ayybcxe5(v=vs.120)
You will want to implment an inline value editor

WPF XAML editor always says Dictionary with custom key type is not a collection

I'm trying to clear up some squigglies in our project's XAML. The issue I'm facing is that we use dictionaries that use a custom type as the key rather than string.
This works fine at runtime, but the XAML editor squigglies the uses of them with the error:
Type 'Dictionary`2' is not a Collection
Example:
<Window x:Class="DictionaryCustomKey.MainWindow"
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:local="clr-namespace:DictionaryCustomKey"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800" Height="450"
DataContext="{x:Static local:MainWindow.TheData}"
mc:Ignorable="d">
<StackPanel>
<ListView ItemsSource="{Binding StringKeyDictionary}" />
<TextBlock Text="String First is:" />
<TextBlock Text="{Binding StringKeyDictionary[First]}" />
<ListView ItemsSource="{Binding MyKeyDictionary}" />
<TextBlock Text="MyKey First is:" />
<TextBlock Text="{Binding MyKeyDictionary[First]}" />
</StackPanel>
</Window>
The very last instance of "First" is squigglied.
Here's the codebehind where I try everything I can imagine to make the editor happy that the key type "MyKey" is suitable as a Dictionary key:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
namespace DictionaryCustomKey
{
public class Data
{
public class MyKeyTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
return new MyKey((string)value);
}
return null;
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value == null)
{
return null;
}
if (destinationType == typeof(string))
{
return ((MyKey)value).String;
}
return null;
}
}
[TypeConverter(typeof(MyKeyTypeConverter))]
public class MyKey : IComparable, IComparable<MyKey>, IComparable<string>, IEquatable<MyKey>, IEquatable<string>, IConvertible
{
public MyKey(string stringKey)
{
m_String = stringKey;
}
public static implicit operator MyKey(string v)
{
return new MyKey(v);
}
public static implicit operator string(MyKey v)
{
return v.m_String;
}
public String String => m_String;
private String m_String;
public override string ToString()
{
return String;
}
public override bool Equals(object obj)
{
if (obj is MyKey)
{
return ((MyKey)obj) == this;
}
if (obj is string)
{
return ((string)obj) == this.String;
}
return false;
}
public bool Equals(MyKey other)
{
return this == other;
}
public override int GetHashCode()
{
return String.GetHashCode();
}
public int CompareTo(object obj)
{
return String.CompareTo(obj);
}
public int CompareTo(MyKey other)
{
return String.CompareTo(other);
}
public int CompareTo(string other)
{
return String.CompareTo(other);
}
public bool Equals(string other)
{
return String.Equals(other);
}
public TypeCode GetTypeCode()
{
return String.GetTypeCode();
}
public bool ToBoolean(IFormatProvider provider)
{
return ((IConvertible)String).ToBoolean(provider);
}
public char ToChar(IFormatProvider provider)
{
return ((IConvertible)String).ToChar(provider);
}
public sbyte ToSByte(IFormatProvider provider)
{
return ((IConvertible)String).ToSByte(provider);
}
public byte ToByte(IFormatProvider provider)
{
return ((IConvertible)String).ToByte(provider);
}
public short ToInt16(IFormatProvider provider)
{
return ((IConvertible)String).ToInt16(provider);
}
public ushort ToUInt16(IFormatProvider provider)
{
return ((IConvertible)String).ToUInt16(provider);
}
public int ToInt32(IFormatProvider provider)
{
return ((IConvertible)String).ToInt32(provider);
}
public uint ToUInt32(IFormatProvider provider)
{
return ((IConvertible)String).ToUInt32(provider);
}
public long ToInt64(IFormatProvider provider)
{
return ((IConvertible)String).ToInt64(provider);
}
public ulong ToUInt64(IFormatProvider provider)
{
return ((IConvertible)String).ToUInt64(provider);
}
public float ToSingle(IFormatProvider provider)
{
return ((IConvertible)String).ToSingle(provider);
}
public double ToDouble(IFormatProvider provider)
{
return ((IConvertible)String).ToDouble(provider);
}
public decimal ToDecimal(IFormatProvider provider)
{
return ((IConvertible)String).ToDecimal(provider);
}
public DateTime ToDateTime(IFormatProvider provider)
{
return ((IConvertible)String).ToDateTime(provider);
}
public string ToString(IFormatProvider provider)
{
return String.ToString(provider);
}
public object ToType(Type conversionType, IFormatProvider provider)
{
return ((IConvertible)String).ToType(conversionType, provider);
}
public static bool operator ==(MyKey lhs, MyKey rhs)
{
return lhs.String == rhs.String;
}
public static bool operator !=(MyKey lhs, MyKey rhs)
{
return !(lhs == rhs);
}
}
public Dictionary<string, object> StringKeyDictionary { get; set; } = new Dictionary<string, object>
{
{"First", "zero"},
{"Second", "one"}
};
public Dictionary<MyKey, object> MyKeyDictionary { get; set; } = new Dictionary<MyKey, object>
{
{new MyKey("First"), "zero"},
{new MyKey("Second"), "one"}
};
}
public partial class MainWindow : Window
{
static public Data TheData { get; set; } = new Data();
public MainWindow()
{
InitializeComponent();
}
}
}
But nothing seems to work.
Edit: An answer is given that makes the squiggly go away, but it reveals there is actually a worse problem: squiggly or not, the designer has trouble displaying the binding: https://developercommunity.visualstudio.com/content/problem/848331/visual-studio-must-be-restarted-after-every-build.html
This appears to be a bug in the XAML editor. Using nested element syntax doesn't have the error:
<TextBlock>
<TextBlock.Text>
<Binding Path="MyKeyDictionary[First]" />
</TextBlock.Text>
</TextBlock>
I would recommend reporting it through the Visual Studio feedback tool.

How to pass CommandParameters to the ViewModel?

I have a command that should switch the current view when it's executed. I binded this command to my buttons like this:
<Button Style="{StaticResource TextButton}" Command="{Binding ViewModel:MainViewModel.OpenItemCommand}" CommandParameter="{Binding Link}"/>
I want to pass Link (the link of the currently selected article) to my command. My command is defined like this:
public class Command : ICommand
{
public event EventHandler CanExecuteChanged;
readonly Predicate<Object> _canExecute;
readonly Action<Object> _executeAction;
public Command(Predicate<Object> canExecute, Action<object> executeAction)
{
_canExecute = canExecute;
_executeAction = executeAction;
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
return _canExecute(parameter);
return true;
}
public void UpdateCanExecuteState()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
public void Execute(object parameter)
{
if (_executeAction != null)
_executeAction(parameter);
UpdateCanExecuteState();
}
}
In my ViewModel I have this:
public ICommand OpenItemCommand
{
get
{
if (_openItemCommand == null)
{
_openItemCommand = new Command.Command(
p => true,
p => OpenItem(_HOW_DO_I_GET_THE_PARAMETER?_)
);
}
return _openItemCommand;
}
set
{
if (_openItemCommand != value)
{
_openItemCommand = value;
RaisePropertyChanged("OpenItemCommand");
}
}
}
private void OpenItem(Uri link)
{
throw new NotImplementedException();
}
When I create the command I need to pass the command parameter (the link) to the Execute method. But how do I get the value of this? I defined the CommandParameter in XAML, but I don't know how to access it.
I really searched through a huge amount of websites but I can't really find the answer.
You should look at the implementation of Prism's DelegateCommand or MVVM light's RelayCommand. With these you would write code like this:
public class ViewModel
{
public ViewModel()
{
OpenItemCommand = new RelayCommand<string>(OpenItem);
}
public RelayCommand<string> OpenItemCommand { get; private set; }
private void OpenItem(string link)
{
Debug.WriteLine(link);
}
}
where string in this case is the type of the parameter.
I'm not sure where the link parameter is coming from but if it's from a control, the value of the control could be bound to a property of your view model, then you don't need a parameter, for example:
public class ViewModel
{
public ViewModel()
{
OpenItemCommand = new RelayCommand(OpenItem);
}
public RelayCommand OpenItemCommand { get; private set; }
public string Link { get; set; }
private void OpenItem()
{
Debug.WriteLine(Link);
}
}
replace
p => OpenItem(_HOW_DO_I_GET_THE_PARAMETER?_)
with
p => OpenItem(p)
that is what the p stands for: parameter

Why does WPF seem to bypass TypeDescriptionProviderAttribute when INotifyPropertyChanged is implemented?

I'm trying to use the [TypeDescriptionProviderAttribute] in order to give my class a custom type descriptor. This works, but when I implement INotifyPropertyChanged WPF seems to ignore the custom type descriptor and go straight for the CLR property (if it exists). Here's a snippet, I'll paste the full example later on:
//[TypeDescriptionProvider(typeof(MyProvider))]
class MyModel : Object
//, INotifyPropertyChanged
//, ICustomTypeDescriptor
{
public string TheProperty { get { return "CLR - TheProperty"; } }
I bind a TextBlock to TheProperty. When I...
Leave everything commented
I see "CLR - TheProperty" as expected.
Use [TypeDescriptionProvider]
I see "MyPropertyDescriptor - TheProperty" as expected.
Use ICustomTypeDescriptor
I see "MyPropertyDescriptor - TheProperty" as expected.
Use ICustomTypeDescriptor and INotifyPropertyChanged
I see "MyPropertyDescriptor - TheProperty" as expected.
Use [TypeDescriptionProvider] and INotifyPropertyChanged
I see "CLR - TheProperty". Why is this? The weird thing is that custom properties without a CLR property are shown normally. My custom type descriptor also returns a "MyPropertyDescriptor - AnotherProperty" which works in all cases because there is no CLR AnotherProperty defined.
In summary, given this XAML
<StackPanel>
<TextBlock Text="{Binding TheProperty}" />
<TextBlock Text="{Binding AnotherProperty}" />
</StackPanel>
AnotherProperty always works as expected because the model does not have a CLR property named "AnotherProperty". TheProperty works as expected except when [TypeDescriptionProvider] and INotifyPropertyChanged are both used.
Here's the full code. It's a bit long but most of it is irrelevant, it's just required by System.ComponentModel
public partial class TestWindow : Window
{
public TestWindow()
{
InitializeComponent();
DataContext = new MyModel();
}
}
//[TypeDescriptionProvider(typeof(MyProvider))]
class MyModel : Object
//, INotifyPropertyChanged
//, ICustomTypeDescriptor
{
public string TheProperty { get { return "CLR - TheProperty"; } }
public event PropertyChangedEventHandler PropertyChanged;
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this);
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this);
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(this);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return TypeDescriptor.GetProperties(this, attributes);
}
public PropertyDescriptorCollection GetProperties()
{
return MyTypeDescriptor.GetCustomProperties();
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
}
class MyProvider : TypeDescriptionProvider
{
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
return new MyTypeDescriptor();
}
}
class MyTypeDescriptor : CustomTypeDescriptor
{
public override PropertyDescriptorCollection GetProperties()
{
return GetCustomProperties();
}
public static PropertyDescriptorCollection GetCustomProperties()
{
return new PropertyDescriptorCollection(
new[] {
new MyPropertyDescriptor("TheProperty"),
new MyPropertyDescriptor("AnotherProperty")
});
}
}
class MyPropertyDescriptor : PropertyDescriptor
{
public MyPropertyDescriptor(string propName)
: base(propName, null)
{
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return typeof(MyModel); }
}
public override object GetValue(object component)
{
return "MyPropertyDescriptor - " + Name;
}
public override bool IsReadOnly
{
get { return true; }
}
public override Type PropertyType
{
get { return typeof(string); }
}
public override void ResetValue(object component)
{
throw new InvalidOperationException("cannot reset value");
}
public override void SetValue(object component, object value)
{
throw new InvalidOperationException("property is readonly");
}
public override bool ShouldSerializeValue(object component)
{
return true;
}
}
Old question, but for people looking for an answer..
Problem is in System.Windows.PropertyPath.ResolvePropertyName(String, Object, Type, Object, Boolean) private method. I have found it in PresentationFramework.dll in .NET 4.0.
Extracted from .NET Reflector:
object propertyHelper = DependencyProperty.FromName(str, ownerType);
if ((propertyHelper == null) && (item is ICustomTypeDescriptor))
{
propertyHelper = TypeDescriptor.GetProperties(item)[str];
}
if ((propertyHelper == null) && ((item is INotifyPropertyChanged) || (item is DependencyObject)))
{
propertyHelper = this.GetPropertyHelper(ownerType, str);
}
if (propertyHelper == null)
{
propertyHelper = TypeDescriptor.GetProperties(item)[str];
}
if (propertyHelper == null)
{
propertyHelper = this.GetPropertyHelper(ownerType, str);
}
if ((propertyHelper == null) && throwOnError)
{
throw new InvalidOperationException(SR.Get("PropertyPathNoProperty", new object[] { ownerType.Name, str }));
}
return propertyHelper;
As you can see, retrieving property identifier (DependencyProperty / PropertyDescriptor / PropertyInfo) goes like this:
Try get DependencyProperty,
If item implements ICustomTypeDescriptor, use TypeDescriptor to get PropertyDescriptor,
If item implements INotifyPropertyChanged or is DependencyObject, use System.Reflection to get PropertyInfo,
Else use TypeDescriptor to get PropertyDescriptor,
Else use System.Reflection to get PropertyInfo,
Else throw exception or return null.
So System.Reflection/PropertyInfo gets precedence over TypeDescriptor/PropertyDescriptor if item implements INotifyPropertyChanged interface. I believe they choose this strategy for performance reasons because PropertyInfo is much more lighter than PropertyDescriptor.
Solution to your problem would be to implement ICustomTypeDescriptor (preferably explicitly) so that it transfers ICustomTypeDescriptor method calls to appropriate TypeDescriptor method calls but not with object parameter, but Type parameter (this.GetType()). This way your TypeDescriptionProvider will be used.

WPF - Lock Position and size on a canvas

I have a canvas that I can create and drop elements onto. I can move them and size them with a thumb or through binding to a property grid.
Now, I need to implement a feature to "lock" position and size. I started with try and set min and max height\width to the actual height\width. The initial effort wasn't so great. And I still don't have a strategy for fixing the location.
So before I go off and wade through 25 approaches that don't work, perhaps someone has a suggestion. I'd much appreciate it.
Thanks,
jeff
You could just disable the Thumb and the PropertyGrid...
Of course, for the PropertyGrid it is not ideal... it would be better to keep it enabled, but read-only, unfortunately the PropertyGrid doesn't have a ReadOnly property... A possible solution would be to wrap your object in a custom type descriptor which would present the properties as read-only. Here are two classes to achieve that (sorry for the long code...) :
ReadOnlyTypeDescriptor :
public class ReadOnlyTypeDescriptor : ICustomTypeDescriptor
{
public ReadOnlyTypeDescriptor(object target)
{
TypeDescriptionProvider provider = TypeDescriptor.GetProvider(target);
_originalDescriptor = provider.GetTypeDescriptor(target);
}
public ReadOnlyTypeDescriptor(ICustomTypeDescriptor descriptor)
{
_originalDescriptor = descriptor;
}
private ICustomTypeDescriptor _originalDescriptor;
private PropertyDescriptor MakeReadOnly(PropertyDescriptor propertyDescriptor)
{
return new ReadOnlyPropertyDescriptor(propertyDescriptor);
}
private PropertyDescriptorCollection MakeReadOnly(PropertyDescriptorCollection propertyDescriptors)
{
var descriptors = propertyDescriptors
.Cast<PropertyDescriptor>()
.Select(pd => new ReadOnlyPropertyDescriptor(pd))
.ToArray();
return new PropertyDescriptorCollection(descriptors, true);
}
#region ICustomTypeDescriptor Members
public AttributeCollection GetAttributes()
{
return _originalDescriptor.GetAttributes();
}
public string GetClassName()
{
return _originalDescriptor.GetClassName();
}
public string GetComponentName()
{
return _originalDescriptor.GetComponentName();
}
public TypeConverter GetConverter()
{
return _originalDescriptor.GetConverter();
}
public EventDescriptor GetDefaultEvent()
{
return _originalDescriptor.GetDefaultEvent();
}
public PropertyDescriptor GetDefaultProperty()
{
return MakeReadOnly(_originalDescriptor.GetDefaultProperty());
}
public object GetEditor(Type editorBaseType)
{
return _originalDescriptor.GetEditor(editorBaseType);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return _originalDescriptor.GetEvents(attributes);
}
public EventDescriptorCollection GetEvents()
{
return _originalDescriptor.GetEvents();
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return MakeReadOnly(_originalDescriptor.GetProperties(attributes));
}
public PropertyDescriptorCollection GetProperties()
{
return MakeReadOnly(_originalDescriptor.GetProperties());
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return _originalDescriptor.GetPropertyOwner(pd);
}
#endregion
}
ReadOnlyPropertyDescriptor :
public class ReadOnlyPropertyDescriptor : PropertyDescriptor
{
public ReadOnlyPropertyDescriptor(PropertyDescriptor descriptor)
: base(
descriptor.Name,
descriptor.Attributes.Cast<Attribute>().ToArray())
{
_originalDescriptor = descriptor;
}
private PropertyDescriptor _originalDescriptor;
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return _originalDescriptor.ComponentType; }
}
public override object GetValue(object component)
{
return _originalDescriptor.GetValue(component);
}
public override bool IsReadOnly
{
get { return true; }
}
public override Type PropertyType
{
get { return _originalDescriptor.PropertyType; }
}
public override void ResetValue(object component)
{
throw new NotSupportedException();
}
public override void SetValue(object component, object value)
{
throw new NotSupportedException();
}
public override bool ShouldSerializeValue(object component)
{
return _originalDescriptor.ShouldSerializeValue(component);
}
}
To show the object target as read-only in the PropertyGrid, just do that :
propertyGrid.SelectedObject = new ReadOnlyTypeDescriptor(target);
It will show the same properties, but they won't be editable...
OK, this solution is probably a little overkill for your needs... but I think it can be handy in some cases ;)

Resources