property is trying to access the value from System.Windows.FrameworkElement - c#

Anyone please try this scenario and share idea to resolve the issue which am facing.
Scenario:
In my class(inherited from Control) just am have declared the property FlowDirection which is the type of BulletGraphFlowDirection(Enum (Forward, Backward)).
I have used the new Keyword to FlowDirection to resolve warning which I get.
Warning 'DirectionSfBulletGraph1.MyClass.FlowDirection' hides inherited member 'System.Windows.FrameworkElement.FlowDirection'. Use the new keyword if hiding was intended.
public enum BulletGraphFlowDirection
{
Forward,
Backward
}
public class MyClass : Control
{
public new BulletGraphFlowDirection FlowDirection
{
get { return (BulletGraphFlowDirection)GetValue(FlowDirectionProperty); }
set { SetValue(FlowDirectionProperty, value); }
}
// Using a DependencyProperty as the backing store for FlowDirection. This enables animation, styling, binding, etc...
public new static readonly DependencyProperty FlowDirectionProperty =
DependencyProperty.Register("FlowDirection", typeof(BulletGraphFlowDirection), typeof(MyClass), new PropertyMetadata(BulletGraphFlowDirection.Backward));
}
Issue:
When I try to set the property FlowDirection value in Xaml page it’s just thrown the error message” Backward is not a valid value for FlowDirection ”.
<local:MyClass x:Name="myClass" FlowDirection="Backward"/>
My guess is the FlowDirection property is trying to access the value from System.Windows.FrameworkElement. FlowDirection'(Enum(LeftToRight,RightToLeft))
Am not getting error when the property value is set through code behind.
myClass.FlowDirection = BulletGraphFlowDirection.Backward;
Why am I getting issue when declared from Xaml page Its very difficult to found root cause of it. Please share me idea to resolve.
Regards,
Jeyasri M

Well, as you said, it tries to use the value of the original FlowDirection.
The reason is that hiding != overriding. So if your element is stored in a collection of type Control and FlowDirection is called on its elements then the Control.FlowDirection will be called and not the MyClass.FlowDirection. Probably your control is handled as Control and not MyClass when it tries to parse the xaml and initialize the view.
When you set the value in code-behind you specify it explicitly that you want to set the MyClass.FlowDirection by using a MyClass typed variable.
If you would instantiate this variable as:
Control myClass = new MyClass();
Then
myClass.FlowDirection = BulletGraphFlowDirection.Backward;
wouldn't work either in my opinion.
As Karmacon suggested, changing the name will solve the problem. (I guess that forces the xaml parser to handle your control as MyClass and not Control)

Related

Creating Binding from Code-Behind not working C# XAML

I have the following code that creates a binding in code-behind. However, it does not seem to work (when the text in PageMarginTextBox is changed, nothing happens, and when the app is loaded, the Padding of newPage is not set to the text of PageMarginTextBox). To make matters worse, no Exceptions are thrown at all. All elements have been defined earlier on.
Binding pageMarginBinding = new Binding
{
Source = PageMarginTextBox,
Path = new PropertyPath("Text"),
};
newPage.SetBinding(ContentControl.PaddingProperty, pageMarginBinding);
//PageMarginTextBox.Text determines the Padding of newPage
How can I fix this? Any solutions would be appreciated. Thanks!
You are trying to Bind PaddingProperty to text. Padding property is of type Thickness and Text property is String.
I am not sure whether you want to bind padding / text, just giving you an idea if you want to bind the Padding.
Binding pageMarginBinding = new Binding
{
Source = PageMarginTextBox,
Path = new PropertyPath("Padding"),
};
newPage.SetBinding(ContentControl.PaddingProperty, pageMarginBinding);
Your problem is because you are trying to assign a string to a Thickness. In XAML the compiler internally translates the string "0,0,2,2" to Thickness object. But in code behind you have to write the code for the conversion yourself.
ThicknessConverter myThicknessConverter = new ThicknessConverter();
PageThickness= (Thickness)myThicknessConverter.ConvertFromString(PageMarginTextBox.Text);
Then you have to bind this to your control. Again this is only half the solution. You need to wire this up with the Binding.
private Thickness _pageThickness;
public Thickness PageThickness
{
get
{
return _pageThickness;
}
set
{
_pageThickness = value;
NotifyPropertyChanged("PageThickness");
}
Then you probably can bind it in XAML

Custom Dependency Property bindings

I am trying to implement a numberBox class inherited from TextBox
public class numberBox : TextBox
And I declared a custom DependencyProperty numberProperty
public static readonly DependencyProperty numberProperty = DependencyProperty.Register("number", typeof(object), typeof(numberBox), new PropertyMetadata(0));
public object number
{
get { return GetValue(numberProperty); }
set { SetValue(numberProperty, value); }
}
In constructor of numberBox, I have a binding to synchronize Text and number
public numberBox()
{
Binding b = new Binding("number");
b.Source = this;
this.SetBinding(TextProperty, b);
}
In one case when I use the numberBox like this way
<BC:numberBox x:Name="numC1" number={Binding ElementName=dg, Path=SelectedItem.C1} />
"dg" is a DataGrid, my goal is that when DataGrid selection changed, the numberBox display the value of selected item
P.S I know I can use DataGrid.SelectionChanged event to achieve the same behavior but I just want to learn more about binding
Everything is working fine so far, when I select different row of DataGrid, the numberBox displays correct value, however, when the numberBox got focus, be edited, after losing focus, the numberProperty binding was gone, which means when DataGrid selected item changed, it doesn't bring the value into numberBox anymore
I set a break point and check "this.GetBindingExpression(numberProperty)" in numberBox, and it returns null after this numberBox be edited and lost focus
Does anyone knows the reason and how should I do to fix this?
Many thanks.
You can fix it by setting
b.Mode = BindingMode.OneWay;
in the constructor of numberBox.
By the binding back of TextProperty to source "number" the binding between numberProperty and the grid is cleared.

Windows 8 - Animating custom property in code-behind

Basically, I want to make bunch of Shapes and make them animated. So I came up with following custom class:
public class FunkyShape : DependencyObject
{
public double Animator
{
get { return (double)GetValue(AnimatorProperty); }
set { SetValue(AnimatorProperty, value); }
}
public static readonly DependencyProperty AnimatorProperty =
DependencyProperty.Register("Animator", typeof(double), typeof(FunkyShape),
new PropertyMetadata(0, new PropertyChangedCallback(Animator_Changed)));
private static void Animator_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
double delta = (double)e.NewValue - (double)e.OldValue;
((FunkyShape)d).ProcessDelta((double)e.NewValue, delta);
}
private void ProcessDelta(double val, double delta)
{
Holder.Width = val;
Holder.Height = val;
// Keep shape centered
HolderPosition.X = delta / 2;
HolderPosition.Y = delta / 2;
}
private Shape Holder;
public TranslateTransform HolderPosition
{
get { return (TranslateTransform)Holder.RenderTransform; }
}
public FunkyShape(Canvas playground, Shape shapeToInit)
{
Holder = shapeToInit;
Holder.Width = 10;
Holder.Height = 10;
Holder.Fill = new SolidColorBrush(Colors.Blue);
Holder.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Center;
Holder.RenderTransform = new TranslateTransform()
{
X = 500,
Y = 500
};
Holder.RenderTransformOrigin = new Point(0.5, 0.5);
// init done
playground.Children.Add(Holder);
Animate();
}
public void Animate()
{
DoubleAnimation g1 = GrowAnimation();
Storyboard sb = new Storyboard();
Storyboard.SetTarget(g1, this);
// CAN'T FIND ANIMATOR PROPERTY
Storyboard.SetTargetProperty(g1, "Animator");
sb.Children.Add(g1);
sb.Begin(); // THROWS EXCEPTION
}
private static DoubleAnimation GrowAnimation()
{
DoubleAnimation growAnimation = new DoubleAnimation();
growAnimation.Duration = TimeSpan.FromMilliseconds(3000);
growAnimation.From = 0;
growAnimation.To = 100;
growAnimation.AutoReverse = true;
growAnimation.EnableDependentAnimation = true;
growAnimation.RepeatBehavior = new RepeatBehavior(5);
return growAnimation;
}
}
However, when I try making an instance of the class and adding it to the canvas, I get Exception - Storyboard.Being() throws it and tells me that it can't find Animator property.
So - what am I doing wrong?
EDIT: After 3 code changes - it is still not working; I get "Cannot resolve TargetProperty Animator on specified object" error. So if somebody knows the answer - please help out by modifying the code. Thanks!
EDIT: OK, after 24 hours of banging head against the wall there is some progress - if I add shape through XAML it animates, but if I add it through code behind (Canvas.Children.Add), it doesn't work. Let me see if I can figure out why.
OK,
I've found the workaround for what is obviously a bug within the framework (although I'm sure some MS employee will post response and say it's a feature/it-is-by-design). Several things need to be done:
Add default/parameter-less constructor
Change base class of FunkyShape to UserControl.
Open up XAML view of the Page class where you want to add shapes
Add one instance of FunkyShape as a child within the Canvas XAML (<tm:FunkyShape /> for example). IT WON'T WORK WITHOUT THIS.
Make an instance of FunkyShape in code-behind, add it to canvas, start animation and enjoy seeing it works
Switch to less buggy technology.
In Windows 8 you cannot animate custom properties without also setting the enabledependentanimation property to true. This is because non-deterministic animations are disabled by default.
Reference: http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.animation.pointanimation.enabledependentanimation.aspx
Yes, you must define this property as a dependency property, not just a regular CLR property. This involves quite a bit of simple boiler plate code. See thus blog post for a complete example:
http://timheuer.com/blog/archive/2012/03/07/creating-custom-controls-for-metro-style-apps.aspx
OK, I had this problem too, but I didn't want to include a public parameterless constructor in my class, so I found another way.
Basically, the issue is that WinRT is a native platform, and it can't do reflection on .NET code. That's why the build process for WinRT apps generates metadata about the types used in XAML (you can find the relevant code in obj/(Debug|Release)/XamlTypeInfo.g.cs).
If a type is never used in XAML, no metadata about this type is generated, which means (among other things) that you can't animate the properties of the type.
If you're writing a class library, you can just include a XAML resource dictionary and declare a dummy instance of the type; it will cause metadata to be generated. However, it requires that the type has a public parameterless constructor, which might not be desirable.
So there is another solution: provide the metadata yourself. There are a several interfaces to implement, and they have many members, so it can be quite tedious to do manually. Fortunately, you don't have to! Here's what you can do:
add a public parameterless constructor to the class (temporarily)
create a XAML ResourceDictionary and declare an instance of the class in it (as described above)
copy the XamlTypeInfo.g.cs file into your project (I renamed it to XamlTypeInfo.cs)
replace the call to the constructor with throw new NotImplementedException()
delete the ResourceDictionary file
remove the public parameterless constructor
And you're done, the animation now works properly.
The process is still quite tedious, so it would be nice to have a tool to do the work for us...
EDIT: much easier solution: apply the [Bindable] attribute to the class. It makes the metadata generator take the type into account even if it's not used in XAML. (ignore the fact that the doc says it's for C++ types; it works just fine on C# classes as well)

Why my control's properties won't change outside its class?

I'm new in C# but not new to coding --being doing it for almost two decades--, and have a problem with properties in a custom control I'm building, which inherits from a Panel. When I put my properties, I can see them in the Designer properties list and can even set them, but when running my little application, it seems these properties values are not used. The same if I change a property programatically: no error but my control does nothing, it is like they are not properly set. However, if I do it programatically whithin the class, they do work. My guess is that something in my properties set/get stuff is not right. Please see the following code chunk of how I'm doing it:
public class ColorStrip : Panel
{
// properties
// ------------------------------------------
// size of color clusters (boxes)
private int _clusterSize = 20;
// controls if show the buttons panel
private Boolean _showButtons;
// property setters/getters
// ------------------------------------------
// clusterSize...
public int clusterSize
{
get { return _clusterSize; }
set { _clusterSize = value; }
}
// showButtons...
public Boolean showButtons
{
get { return _showButtons; }
set { Console.Write(_showButtons); _showButtons = value; }
}
....
So in my form, for instance in the load or even in a click event somewhere, if I put colorStrip1.showButtons = false; or colorStrip1.showButtons = true; whatever (colorStrip1 would be the instance name after placing the control in the form in design mode)... console.write says always 'false'; Even if I set it in the design properties list as 'true' it will not reflect the settled value, even if I default it to true, it will never change externally. Any ideas? Non of the methods get the new and externally settled property value neither, obviously the getter/setter thing is not working. Seems to me I'm not doing right the way I set or get my properties outside the class. It works only inside it, as a charm...Any help...very appreciate!
Cheers
lithium
p.s. TO CLARIFY SOLUTION:
Setting the property in this case didn't work because I was trying to use a new set value within the constructor, which seems can't get the new values since it is, well, building the thing. If I change the property value in Design mode > Property editor or in code externally to the object, say in it's parent form's load event, it will change it but readable for all methods except the constructor, of course :)
It's likely an issue of the order of execution. Your property setter just sets a variable, but doesn't actually trigger anything on the control to update the state related to this variable (e.g. adding or showing the buttons I assume).
When you set the property befre the rest of the initialization is done, the value is being used, otherwise it isn't because during the initial go the default value is still the property value.
You need to act on the setter, here's some pseudocode to illustrate:
set {
_showButtons = value;
if (alreadyInitialized) {
UpdateButtons();
}
}
Note: make sure to first set the value, then act - otherwise you end up using the old value (just like your Console.Write() is doing).
The quoted code doesn't look problematic. Are you sure you're referencing the same instance of ColorStrip? Also, check your .Designer.cs file to ensure that the code setting the property is there.
In fact, try simplifying your code by using auto-implementing properties:
public int clusterSize { get;set;}
public Boolean showButtons {get;set;}
public ColorStrip() { ... clusterSize = 20; ... }

How to resolve bound object from bindingexpression with WPF?

Hi does anyone know if there are any inbuilt classes for resolving a bound object from a bindingexpression and it's DataItem and property path?
I'm attempting to write a Blend 3 behavior for textboxes which automatically invokes methods on an object bound to the textbox Text property.
The textbox is bound to a property on a viewmodel class. What I want to do is resolve the viewmodel class from the binding expression and then make calls on this.
I first retrieve the binding expression from the behavior's associated object like so:
private BindingExpression GetTextBinding()
{
return this.AssociatedObject.GetBindingExpression(TextBox.TextProperty);
}
Having done this, if we look at the binding expression, we can see it has a reference to the data context via the binding expression's DataItem property.
In addition, we have the relative path of the property which is bound on the binding expression's parent binding.
So, we can get this information:
var bindingExpression = GetTextBinding();
object dataContextItem = bindingExpression.DataItem;
PropertyPath relativePropertyPath = bindingExpression.ParentBinding.Path;
Now, this property path could potentially be a deeply nested and complex path, which I would very much like to avoid having to (re?)implement resolution of. I've searched around the .NET documentation and bounced around the assemblies with reflector, all to no avail - I can't find what surely must exist - there's got to be some class which performs the resolution of the path for the dataitem (the data context).
Does anyone know where this might exist? Any suggestions for alternative ways of resolving the bound object?
Note, I'm trying to get at the bound object which is the parent of the bound property (the string in this case) - I can obviously easily get at the bound value, but it's the parent I need.
Thanks in advance for any help!
Phil
For people in the future who stumble on this question:
When .NET 4.5 becomes available it will have a number of new properties on the BindingExpression to greatly simplify what you are looking for.
ResolvedSource - The object that is actually being bound to, helpful when you have a binding source like 'grandparent.parent.me.Name'. This would return the 'me' object.
ResolvedSourcePropertyName - The name of the property on the ResolvedSource that is bound to. In the case above, "Name".
Similarly, there will be Target, and TargetName properties.
With these helper properties on BindingExpression you could use some shorter and much more simplified reflection that is more likely to work in extreme situations (indexers).
Below is a quick implementation for an extension method that will do just what you are looking for. I couldn't find anything related to this either. The method below will always return null if for some reason the value cannot be found. The method won't work when the path includes []. I hope this helps!
public static T GetValue<T>(this BindingExpression expression, object dataItem)
{
if (expression == null || dataItem == null)
{
return default(T);
}
string bindingPath = expression.ParentBinding.Path.Path;
string[] properties = bindingPath.Split('.');
object currentObject = dataItem;
Type currentType = null;
for (int i = 0; i < properties.Length; i++)
{
currentType = currentObject.GetType();
PropertyInfo property = currentType.GetProperty(properties[i]);
if (property == null)
{
currentObject = null;
break;
}
currentObject = property.GetValue(currentObject, null);
if (currentObject == null)
{
break;
}
}
return (T)currentObject;
}
Just for further information, PropertyPath resolution is handled by an internal MS class called PropertyPathWorker, which lives in PresentationFramework under MS.Internal.Data. If you open it up in Reflector, you can see it's quite complicated, so I wouldn't recommend trying to duplicate its functionality. It's tightly coupled with the overall Binding architecture.
The most robust way to support all property path syntax--including attached dependency properties and hierarchical traversal--is probably to create a dummy DependencyObject with a DependencyProperty. You can then create a Binding from your 'owner' path to the dummy dependency property, create a new BindingExpression and then call the expression's UpdateTarget. It's a rather heavy way of accomplishing what, on the surface, looks like a simple task, but I think there are a lot of hidden gotchas in the way property paths are resolved for binding.
I believe this other StackOverflow solution posted here may also work for you.
Copied code block for reference, read original post for more details provided by Thomas Levesque.
public static class PropertyPathHelper
{
public static object GetValue(object obj, string propertyPath)
{
Binding binding = new Binding(propertyPath);
binding.Mode = BindingMode.OneTime;
binding.Source = obj;
BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, binding);
return _dummy.GetValue(Dummy.ValueProperty);
}
private static readonly Dummy _dummy = new Dummy();
private class Dummy : DependencyObject
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(Dummy), new UIPropertyMetadata(null));
}
}
As Dan Bryant already noted, PropertyPath resolution is tightly coupled with the overall binding architecture.
If you need the exact same resolution as WPF does it, you should probably use Thomas Levesque's answer to this question.
However, if you just need general path resolution, you can use a nuget package Pather.CSharp I developed that does exactly that.
It is essentially similar to zhech's answer, but more sophisticated.
Its main method is Resolve on the Resolver class. Passing in the target object and a path as string returns the desired result.
An Example:
IResolver resolver = new Resolver();
var target = new { Property1 = new { Property2 = "value" } };
object result = r.Resolve(target, "Property1.Property2");
It also supports collection access via index or dictionary access via key.
Example paths for these are:
"ArrayProperty[5]"
"DictionaryProperty[Key]"
Just in case others find it useful, below is a quick one liner extension method for .net4.5 (and beyond) to retrieve the bound value..
public static object GetValue(this BindingExpression bindingExpression)
{
return bindingExpression?.ResolvedSource.GetType().GetProperty(bindingExpression.ResolvedSourcePropertyName)?.GetValue(bindingExpression.ResolvedSource);
}

Categories

Resources