How can I get the x:Name of an object from code? - wpf

Given a reference to an object defined in XAML, is it possible to determine what (if any) x:Name the object has, or can I only do this by accessing the FrameworkElement.Name property (if the object is a FrameworkElement)?

One approach you could take is to first check if the object is a FrameworkElement, and if not, try reflection to get the name:
public static string GetName(object obj)
{
// First see if it is a FrameworkElement
var element = obj as FrameworkElement;
if (element != null)
return element.Name;
// If not, try reflection to get the value of a Name property.
try { return (string) obj.GetType().GetProperty("Name").GetValue(obj, null); }
catch
{
// Last of all, try reflection to get the value of a Name field.
try { return (string) obj.GetType().GetField("Name").GetValue(obj); }
catch { return null; }
}
}

Related

Silverlight: How to Make a ShallowCopy of a UIElement

I need to add a UIElement to two different canvases, but one UIElement can only be a child of ONE canvas, so I have to create a ShallowCopy (DeepCopy not needed) of the UIElement.
I want to use MemberwiseClone, but it's protected, I cannot use it.
I also want to define an extension method UIElement.ShallowCopy, but it sill still call MemberwiseClone, which is protected again.
EDIT:
Tried all the following, but all of them failed in Silverlight environment:
// System.Runtime.Serialization.InvalidDataContractException was unhandled by user code
// Message=Type 'System.Windows.UIElement' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.
public static T CloneEx<T>(this T obj) where T : class
{
T clone;
DataContractSerializer dcs = new DataContractSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream())
{
dcs.WriteObject(ms, obj);
ms.Position = 0;
clone = (T)dcs.ReadObject(ms);
}
return clone;
}
// This one also throws Access/Invoke exceptions
private readonly static object _lock = new object();
public static T MemberwiseCloneEx<T>(this T obj) where T : class
{
if (obj == null)
return null;
try
{
Monitor.Enter(_lock);
T clone = (T)Activator.CreateInstance(obj.GetType());
PropertyInfo[] fields = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (PropertyInfo field in fields)
{
object val = field.GetValue(obj, null);
field.SetValue(clone, val, null);
}
return clone;
}
finally
{
Monitor.Exit(_lock);
}
}
// System.MethodAccessException was unhandled by user code
// Message=Attempt by method 'ToonController.ControllerUtils.MemberwiseCloneEx<System.__Canon>(System.__Canon)' to access method 'System.Object.MemberwiseClone()' failed.
public static T MemberwiseCloneEx<T>(this T obj) where T : class
{
if (obj == null)
return null;
MethodInfo mi = obj.GetType().GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
if (mi == null)
return null;
return (T)mi.Invoke(obj, null);
}
if you have something that you want to use in multiple ui elements, 'sync them up' then you should create a ViewModel or something similar. This viewmodel would be set to the datacontext of any element you want to use it. Then your shallow reference is simple and you can just create two independent UI elements binding to the same data.

Silverlight 4 Late bound operations cannot be performed... and no examples help?

I have tried all variaions that I can find others using, and frankly they all seem to boil down to what I already have.
I want a generic system for invoking methods based on generic inputs. Not sure that really captures it fully, but not sure how to state it.
Here we go:
I want to make breadcrumbs from a params of Expression>
here, SelectedDivisions is ObservableCollection and ModelId is long?.
So, the point is that I want to feed in a list of varying properties, have them processed by the data class such that each is processed by the appropriate method inside of data
data.MakeBreadCrumbs(() => dc.SelectedDivisions, () => dc.ModelId);
data contains the following code:
public void MakeBreadCrumbs(params Expression<Func<object>>[] propertyExpressions) {
foreach (Expression<Func<object>> propertyExpression in propertyExpressions) {
MemberExpression member = propertyExpression.Body as MemberExpression;
if (member == null) {
UnaryExpression uExp = propertyExpression.Body as UnaryExpression;
member = uExp.Operand as MemberExpression;
}
PropertyInfo propInfo = member.Member as PropertyInfo;
Type[] propTypes = propInfo.PropertyType.GetGenericArguments();/
MethodInfo methodInfo = typeof(BreadcrumbData).GetGenericMethod("MakeBreadCrumb", new Type[] { propInfo.PropertyType, typeof(string) }); //
if (methodInfo.IsGenericMethod) {
methodInfo.MakeGenericMethod(propTypes[0]);
}
ConstantExpression ce = Expression.Constant(propertyExpression.Compile().Invoke());
string criterionName = ReadCriterionName(propertyExpression);
methodInfo.Invoke(this, new object[] { ce.Value, criterionName });
}
the last line fails with "Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true." when I am processing the property expression for the ObservableCollection item.
Here are the methods defined in the data class which are available, and which are correctly selected, but the one for the ObservableCollection fails on invocation
(LookupTypeBase is a class particular to my solution, but insert any type here that works with the type of a fake ObservableCollection property)
public void MakeBreadCrumb<T>(ObservableCollection<T> selections, string criterionName) where T : LookupTypeBase {...}
public void MakeBreadCrumb(long? value, string criterionName) {...}
public static class xxx {
public static MethodInfo GetGenericMethod(this Type type, string name, Type[] parameterTypes) {
var methods = type.GetMethods();
foreach (var method in methods.Where(m => m.Name == name)) {
var methodParameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
if (methodParameterTypes.SequenceEqual(parameterTypes, new SimpleTypeComparer())) {
return method;
}
}
return null;
}
private class SimpleTypeComparer : IEqualityComparer<Type> {
public bool Equals(Type x, Type y) {
return x.Assembly == y.Assembly && x.Namespace == y.Namespace && x.Name == y.Name;
}
public int GetHashCode(Type obj) {
throw new NotImplementedException();
}
}
}
It's silverlight 4 so you have latebound method invocation with the dynamic keyword, which should make this easy and fast:
BreadcrumbData.MakeBreadCrumb((dynamic)ce.Value, (dynamic)criterionName);
Aside from that i think your issue is that MakeGenericMethod returns a new methodinfo which you ignore instead of keeping around like so:
if (methodInfo.IsGenericMethod) {
methodInfo = methodInfo.MakeGenericMethod(propTypes[0]);
}

WPF ItemsControl ItemTemplate Validation

Is there a way to determine if an ItemsControl has any child controls with validation errors? I would like to bind this value (boolean) to the IsEnabled property on a Button.
I've recently used the code from this previous SO answer Detecting WPF Validation Errors to good effect for exactly this.
I have a user control which contains a DataGrid. The usercontrol exposes a IsValid property which contains a getter, that simply calls the static IsValid function passing in the DataGrid as the DependencyObject:
public class MyControl : UserControl
{
public bool IsValid
{
get { return Validator.IsValid(MyDataGrid); }
}
}
The control's IsValid property can then be checked by the CanExecute function of the command you bind to the button you want to enable/disable.
My only issue with the code that i linked to was that it actually evaluates the validations on the bindings, this means as soon as you run it any field that is technically invalid but hasn't yet been invalidated (i.e. the user may not have entered any data in that field yet because they haven't got to it) will now be in an invalid state - i haven't yet looked at a way to avoid or mitigate this.
Edit:
here is an updated version that doesn't invalidate the controls as i mentioned previously. I've simply commented out/slightly changed some lines but left everything in there so you can see the difference. Note that this should also perform faster as you will be exiting the moment you find the first invalid binding.
public static bool IsValid(DependencyObject parent)
{
// Validate all the bindings on the parent
bool valid = true;
LocalValueEnumerator localValues = parent.GetLocalValueEnumerator();
while (localValues.MoveNext())
{
LocalValueEntry entry = localValues.Current;
if (BindingOperations.IsDataBound(parent, entry.Property))
{
Binding binding = BindingOperations.GetBinding(parent, entry.Property);
foreach (ValidationRule rule in binding.ValidationRules)
{
ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null);
if (!result.IsValid)
{
//BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property);
//System.Windows.Controls.Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null));
//valid = false;
return false;
}
}
}
}
// Validate all the bindings on the children
for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (!IsValid(child))
{
//valid = false;
return false;
}
}
//return valid;
return true;
}
I don't know why, slugter's answer didn't work for me (LocalValueEnumerator returned some properties but never the binded ones, like Text).
I managed to get it working with this code (derived from this answer):
public static bool IsValid(DependencyObject obj)
{
// The dependency object is valid if it has no errors,
//and all of its children (that are dependency objects) are error-free.
return !Validation.GetHasError(obj) &&
GetVisualTreeChildren(obj)
.OfType<DependencyObject>()
.All(child => IsValid(child));
}
//VisualTreeHelper don't have a method to get all the children of a visual object
private static IEnumerable GetVisualTreeChildren(DependencyObject parent)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
yield return VisualTreeHelper.GetChild(parent, i);
}

Inherited Control Visible/Enabled Property Value Always True: PropertyGrid

I have created a custom WinForms hosting environment. Which has a toolbox and a PropertyGrid.
The controls displayed in the Toolbox are inherited from existing WinForm controls.
DropDownList Source:
public interface IPropertyFilter : ICustomTypeDescriptor
{
PropertyDescriptorCollection FilterProperties(PropertyDescriptorCollection pdc);
List<string> GetPropertiesToShow();
}
[Serializable]
public class DropDownList : System.Windows.Forms.ComboBox, IPropertyFilter
{
public DropDownList()
{
}
#region IPropertyFilter Members
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this, attributes, true);
return FilterProperties(pdc);
}
PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this, true);
return FilterProperties(pdc);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public PropertyDescriptorCollection FilterProperties(PropertyDescriptorCollection pdc)
{
// Filter out properties that we do not want to display in PropertyGrid
return ControlDesignerHelper.GetBrowsableProperties(pdc, GetPropertiesToShow());
}
// Determines what properties of this control has to be shown in PropertyGrid
public List<string> GetPropertiesToShow()
{
// get a list of common properties that we want to show for all controls
List<string> browsableProps = ControlDesignerHelper.GetBasePropertiesToShow();
// add properties that are specific to this controls
browsableProps.Add("Items");
browsableProps.Add("AutoPostBack");
browsableProps.Add("AppendDataBoundItems");
browsableProps.Add("DataTextField");
browsableProps.Add("DataValueField");
return browsableProps;
}
#endregion
}
I have implemented ICustomTypeDescriptor to filter out properties that I do not want to show in the PropertyGrid.
Problem:
I am facing problem while serializing values of Enabled & Visible properties that are inherited from System.Windows.Forms.Control class.
WriteProperties Method (BasicDesignerLoader):
private void WriteProperties(XmlDocument document, PropertyDescriptorCollection properties, object value, XmlNode parent, string elementName)
{
foreach (PropertyDescriptor prop in properties)
{
System.Diagnostics.Debug.WriteLine(prop.Name);
if (prop.ShouldSerializeValue(value))
{
string compName = parent.Name;
XmlNode node = document.CreateElement(elementName);
XmlAttribute attr = document.CreateAttribute("name");
attr.Value = prop.Name;
node.Attributes.Append(attr);
DesignerSerializationVisibilityAttribute visibility = (DesignerSerializationVisibilityAttribute)prop.Attributes[typeof(DesignerSerializationVisibilityAttribute)];
switch (visibility.Visibility)
{
case DesignerSerializationVisibility.Visible:
if (!prop.IsReadOnly && WriteValue(document, prop.GetValue(value), node))
{
parent.AppendChild(node);
}
break;
case DesignerSerializationVisibility.Content:
object propValue = prop.GetValue(value);
if (typeof(IList).IsAssignableFrom(prop.PropertyType))
{
WriteCollection(document, (IList)propValue, node);
}
else
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(propValue, propertyAttributes);
WriteProperties(document, props, propValue, node, elementName);
}
if (node.ChildNodes.Count > 0)
{
parent.AppendChild(node);
}
break;
default:
break;
}
}
}
}
Problem # 1: The ShouldSerializeValue method for the Enabled & Visible property always returns false.
Problem # 2: Even if I skip the ShouldSerializeValue method check the GetValue method of the PropertyDescriptor always returns True.
Current Workaround:
As a workaround I have currently made the Enabled & Visible properties hidden using the BrowsableAttribute, and created two other boolean properties and used the DisplayNameAttribute to change their display name to be Enable & Visible.
But for this workaround I have to write these snippets in every control.
Am I missing something or doing anything wrong? Why are the Enabled & Visible property do not change?
You will find a long discussion about this issue here. (dead link, can't find a new one)
This MSDN page aldo makes this remark:
The InheritedPropertyDescriptor class
modifies the default value of a
property, so that the default value is
the current value at object
instantiation. This is because the
property is inherited from another
instance. The designer defines
resetting the property value as
setting it to the value that was set
by the inherited class. This value may
differ from the default value stored
in metadata.
ShouldSerializeValue's return value is based on the difference between the current value and the default value so I think this is directly related to your problem.
I hope this will help you figure out what happens in your own context.

WPF Combobox SelectedItem hell

Im having a very funny issue in WPF
Im creating a Combobox through code, and adding it to a control.
When I set the Combobox.SelectedItem or Combobox.SelectedIndex or Combobox.SelectedValue I am unable to select another option from the Combox items.
ForeignKeyDisplayAttribute attribute = (ForeignKeyDisplayAttribute)this.GetAttribute(typeof(ForeignKeyDisplayAttribute), property);
if (attribute != null)
{
ForeignKeyDisplayAttribute fkd = attribute;
Type subItemType = fkd.ForeignKeyObject;
contentControl = new ComboBox();
object blankItem = System.Activator.CreateInstance(subItemType, new object[] { });
System.Reflection.MethodInfo method = subItemType.GetMethod("Load", new Type[] { typeof(int) });
object innerValue = method.Invoke(null, new object[] { value });
System.Collections.IList selectedSubItems = (System.Collections.IList)subItemType.GetMethod("Load", Type.EmptyTypes).Invoke(null, new object[] { });
selectedSubItems.Insert(0, blankItem);
((ComboBox)contentControl).SelectedValuePath = fkd.IdField;
((ComboBox)contentControl).DisplayMemberPath = fkd.DescriptionField;
((ComboBox)contentControl).ItemsSource = selectedSubItems;
((ComboBox)contentControl).InvalidateVisual();
// If I use any of the two below lines or SelectedItem then I can't change the value via the UI.
((ComboBox)contentControl).SelectedIndex = this.FindIndex(selectedSubItems, value);
((ComboBox)contentControl).SelectedValue = value;
}
Any idea's as to how I can fix this?
Well found the answer.
Turns out I had incorrectly coded the overridden object Equals method, and it was always returning false.
public override bool Equals(object obj)
{
if (obj is IId)
return this.Id.Equals(((IId)obj).Id);
return false;
}
should have been
public override bool Equals(object obj)
{
if (obj.GetType() == this.GetType() && obj is IId)
return this.Id.Equals(((IId)obj).Id);
return false;
}
Do you have Bindings on those ComboBox items in GUI? Then the simple reason is: setting a value manually in the code behind destroys the binding.
Workarround:
Before setting the value manually you can get the binding with the BindingOperations static functions.
Binding b = BindingOperations.GetBinding(yourComboBox, ComboBox.SelectedItemProperty);
// do your changes here
BindingOperations.SetBinding(yourComboBox, ComboBox.SelectedItemProperty, b);
Jan

Resources