UWP InvalidCastException when using XAML databinding - c#

Can anyone explain how XAML data binding expressions are evaluated? I have a control with a registered property, VisualState.
public CardStates VisualState
{
get
{
return (CardStates)this.GetValue(VisualStateProperty);
}
set
{
this.SetValue(VisualStateProperty, value);
}
}
public static readonly DependencyProperty VisualStateProperty = DependencyProperty.RegisterAttached("VisualStateProperty", typeof(CardStates), typeof(StateManager), new PropertyMetadata(null, (s, e) => { }));
In the xaml I attempt to bind a value to this property. State exists parent's DataContext object.
<local:CardControl VisualState="{Binding State.Value}" />
The generated code in XamlTypeInfo.g.cs looks like this
private void set_4_CardControl_VisualState(object instance, object Value)
{
var that = (global::MeetEric.UI.Controls.CardControl)instance;
that.VisualState = (global::MeetEric.ViewModels.CardStates)Value;
}
This code throws an InvalidCastException because the value of Value is a Windows.UI.Xaml.Data.Binding object.
Am I missing something obvious to enable working with data bindings? Do I need some form of converter?

Try to change typeof(StateManager) to typeof(CardControl). (at DependencyProperty.RegisterAttached)
This argument requires the owner of the DependencyProperty.

This error occurs when you try to using Binding with a regular property. In order for the binding to resolve to the right type, you need to be binding to a DependencyProperty.
As others have mentioned, your syntax is a bit off which is probably causing the problem here.
This error is certainly very cryptic. I just hit it in my own project, but as soon as I changed my property to a dependency one, it worked perfect.

Related

Dependency property Canvas not binding

I'm making a tool where I can edit images, so I have a custom Control to draw on a canvas. For this control I bound the resolution and the color of the brush which work perfectly fine, but when I try to bind a Canvas I always get null in myViewModel.
I tried binding a List instead, but that didn't work either. I've been trying to figure out what the problem is for about 12 hours now so I think it's time to ask you guys for help so I can maybe figure out what's wrong.
I used to work with singletons for my viewmodels, and then I could link to the Canvas just fine, but then I realised this is a bad way of working so I tried to bind it with dependency properties.
Whenever I try to access the Canvas it has a value of null, even though I initialized it.
Control
public static readonly DependencyProperty CanvasProperty = DependencyProperty.Register(
"CanvasToDraw", typeof(Canvas), typeof(DrawCanvas), new PropertyMetadata(default(Canvas), null, null));
public Canvas CanvasToDraw
{
get { return (Canvas)GetValue(CanvasProperty); }
set { SetValue(CanvasProperty, value); }
}
public DrawCanvas()
{
InitializeComponent();
CanvasToDraw = CanvasGrid;
}
XAML
<Controls:DrawCanvas x:Name="DrawCanvas1" Resolution="{Binding Resolution}" CanvasToDraw="{Binding DrawingCanvas}" RectangleList="{Binding RectangleList, ElementName=DrawCanvas1}" ColorToDraw="{Binding SelectedColor}" HorizontalAlignment="Left" Height="256" Margin="10,40,0,0" VerticalAlignment="Top" Width="256" />
VIEWMODEL
private Canvas _drawingCanvas;
public Canvas DrawingCanvas
{
get { return _drawingCanvas; }
set
{
_drawingCanvas = value;
OnPropertyChanged("DrawingCanvas");
}
}
I can provide more code if needed, but this is most of the relevant code. And yes I do use INotifyPropertyChanged, since I have other things that I bound that do work.
I've been looking for a solution for so long, but the answer was so simple. I assumed the binding was two way, but appareantly I had to set Mode=TwoWay and everything worked like a charm. (Linking an observeablecollection, not a Canvas)

Dependency Property PropertyChangedCallback exception

I have the following DependencyProperty in a custom control:
public bool HasConnection
{
get { return (bool)GetValue(HasConnectionProperty); }
set { SetValue(HasConnectionProperty, value); }
}
public static readonly DependencyProperty HasConnectionProperty =
DependencyProperty.Register(
"HasConnection",
typeof(bool),
typeof(NetworkNode),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(HasConnectionChangedCallBack)));
private static void HasConnectionChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
NetworkNode nn = (NetworkNode)d;
Ellipse el = nn.GetTemplateChild("PART_inner") as Ellipse;
if (el.PART_inner.Visibility == ...) <-- exception el is null
//..code..
}
Runs fine, but if I change the property in Properties panel of my custom control, at run time throws an exception: Object reference not set to an instance of an object.
Edit1:
Forgot to add one line of code in the post Ellipse el = nn.GetTemplateChild("PART_inner") as Ellipse;
Edit2:
Creating a BooleanToVisibilityConverter and using Binding in Generic.xaml works, but the HasConnectionChangedCallBack method is now empty/useless.
Visibility="{Binding HasConnection, Converter={StaticResource BooleanToVisibiltyConverter}, RelativeSource={RelativeSource TemplatedParent}}"
Edit3:
Found a posible fix. The property callback method is called first then the OnApplyTemplate() method, so no more exceptions thrown or error in xaml.
In OnApplyTemplate() I add
if (this.HasConnection)
PART_inner.Visibility = System.Windows.Visibility.Visible;
else
PART_inner.Visibility = System.Windows.Visibility.Hidden;
Do this
private static void HasConnectionChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == null)
return;
NetworkNode nn = (NetworkNode)d;
if (nn == null || nn.Part_inner == null )
return;
if (nn.PART_inner.Visibility == ...) <-- exception
//..code..
}
The reason for the exception is that when the property is set through the XAML parser, the content of the UserControl has not been instantiated.
The XAML parser works its way from top to bottom through the XAML. A UserControl is just a shortcut for the XAML that defines it, so, at the time when the XAML parser sets HasConnection=True on the outer control, its content has not yet been instantiated, so PART_Inner doesn't yet exist.
The solution is to define the relationship between HasConnection and whatever depends on it in the UserControl in a way that keeps the instantiation sequence in mind. For example, if PART_Inner is a UserControl, you could search for its parent of type NetworkNode in its Loaded event, so that HasConnection can be evaluated then. This is probably the solution which requires the least changes to your existing code. Leave the change handler as it is now, including the safety code, and add logic to the contained control which reads the start value from its ancestor.
Other options would be to not use a DependencyPropertyChanged callback at all, but create a Binding on the Visibility property using a RelativeSource typed FindAncestor and a BooleanToVisibilityConverter. Still another idea would be to use a Trigger.

How to hide part of a WPF control conditionally?

I have a control in an assembly that I can't change that is very similar to the .NET DateTimePicker. I want to hide the time picker portion of that control when a certain condition is met (Property value on my ViewModel). The control looks like this:
[TemplatePart(Name = "PART_DatePicker", Type = typeof (DatePicker))]
[TemplatePart(Name = "PART_TimePicker", Type = typeof (TimePicker))]
public class MyDateTimePicker : Control {/*...*/}
This answer shows a nice way to always hide a PART of a control, but I want to do it dynamically:
How to hide a part of a WPF control
I imagine there are a few ways to do this. What I want is something minimal (like in the linked question's answer) as well as something that doesn't violate MVVM. System.Interactivity behaviors and triggers are fair game.
Create a new control extending the previous one
public sealed class MySuperiorDateTimePicker : MyDateTimePicker
{
//....
Add a DependencyProperty that you can bind to your ViewModel's state
public static readonly DependencyProperty HideItProperty =
DependencyProperty.Register(
"HideIt",
typeof(bool),
typeof(MySuperiorDateTimePicker ),
new UIPropertyMetadata(false, HideItPropertyChanged));
//snip property impl
Wait for the property to change, then hide your UI
private static void HideItPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
(d as MySuperiorDateTimePicker).OnHideItChanged((bool)e.OldValue,
(bool)e.NewValue);
}
private void OnHideItChanged(bool oldValue, bool newValue)
{
if(BusyTemplate == null)
return;
FindTimePicker().Visibility = newValue ? Visibility.Visible :
Visibility.Collapsed;
}
private UIElement FindTimePicker()
{
//snip null checks
return GetTemplateChild("PART_TimePicker") as UIElement;
}
Be careful with FindTimePicker as your DP might change before the control is loaded, and GetTemplateChild will return null. The usual thing to do is, in OnHideItChanged, if GetTemplateChild returns null use Dispatcher.BeginInvoke to re-run the event handler later on (ApplicationIdle or earlier).
When you find yourself saying "How can I do UI work using MVVM" stop and rethink your true goals. MVVM != no codebehind, no custom controls, etc.
One solution would be to hide it with the help of a DataTrigger defined in the datatemplate, so that when a certain value in the datacontext of the control is set to true/false then you will hide/show the part.
A quick search and i found some links that you might find useful:
http://zamjad.wordpress.com/2010/06/22/conditionally-hide-controls-from-data-template/
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/ae2dbfb7-5dd6-4352-bfa1-53634289329d/
The solution that worked for me was to edit the style of the control. Using Blend, I edited a copy of the style of the DateTimePicker, and added a binding to Visibility of the TimePicker that looks at my VM and converts the value of the enumeration.

DependencyProperty.Register textbox in XAML

I am trying to get a textbox to display encrypted data and save any changes back to the document after encrypting it. This is what I have:
XAML:
<TextBox Text="{Binding Path=UID, Mode=TwoWay}" Name="txtUID" Width="70"/>
Code behind:
public DependencyProperty UIDProperty = DependencyProperty.Register("UID", typeof(string), typeof(MainWindow), new FrameworkPropertyMetadata(""));
private string UID
{
get { return Encryption.Decrypt((string)GetValue(UIDProperty)); }
set { SetValue(UIDProperty, Encryption.Encrypt(value)); }
}
The problem is when the form loads and when I change the value nothing happens. The text box remains blank and the code never stops at the break points I set to catch the get and set of UID. What am I not doing right?
The "CLR-wrappers" for dependency properties can only get called through code. The XAML parser is used the direct call of the DependencyObject.GetValue and DependencyObject.SetValue methods.
To accomplish your task you can extend your binding with the ValueConverter.
Look into debugging databindings, if a binding is broken you should at least be able to retrieve the binding error.
There are various possible reasons, one of them being that the DataContext is not the control with the property. The fact that the breakpoint is not hit means nothing, that CLR property is just for your convenience (you should not place any custom code in it), if you do not use it in your code noone will, the binding system uses something like SetBinding.
the property string UDI is not being called because it is private. For the XAML to have access you need to make it public. And you need to set the datacontext to the code behind.
There may a problem with DependencyProperty but if the get and set are not called on UID then it is not getting that far. First try without encyption to at least get binding debugged.
<TextBox Text="{Binding Path=UID, Mode=TwoWay}" Name="txtUID" Width="70"/>
Code behind:
public DependencyProperty UIDProperty = DependencyProperty.Register("UID", typeof(string), typeof(MainWindow), new FrameworkPropertyMetadata(""));
string uid = string.empty;
public string UID
{
get
{
//return Encryption.Decrypt((string)GetValue(UIDProperty));
Debug.WriteLine("get called");
return uid;
}
set
{
// SetValue(UIDProperty, Encryption.Encrypt(value));
Debug.WriteLine("set called");
if(uid != value)
{
uid = value;
NotifyPropertyChanged("UID");
}
}
}
Notice UID is a public (not private property).

When to render custom WPF TextBlock?

When should I be building Inlines in a TextBlock? I have a TextBlock-derived class that, when given text in a certain field, call it MyText, converts the text into a set of inlines when MyText has changed.
Whenever MyText changes, I clear the Inlines and build them, colorizing each word as needed. For this example, consider:
private void MyTextBlock_MyTextChanged(object sender, EventArgs e)
{
Inlines.Clear();
if (!string.IsNullOrEmpty(this.MyText))
{
var run = new Run();
run.Foreground = Brushes.DarkRed;
run.Text = this.MyText;
Inlines.Add(run);
}
}
This has worked very well. However, recently we placed the Control into a DataGrid, and some strange things have started happening. Apparently the DataGrid swaps out the context and for the most part this works. However, when we add or delete data from the DataGrid ItemsSource, something goes awry, and TextChanged doesn't seem like it is called (or at least not called at the same time). MyText can be one value, and the Inlines either blank or a different value.
I think that the place to build the Inlines is NOT during MyTextChanged, but maybe when the rendering of the Control starts. I've also tried when the DataContextChanged, but this does not help.
In my constructor, I have
this.myTextDescriptor = DependencyPropertyDescriptor.FromProperty(
MyTextProperty, typeof(MyTextBlock));
if (this.myTextDescriptor != null)
{
this.myTextDescriptor.AddValueChanged(this, this.MyTextBlock_MyTextChanged);
}
corresponding to a dependency property I have in the class
public string MyText
{
get { return (string)GetValue(MyTextProperty); }
set { SetValue(MyTextProperty, value); }
}
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register("MyText", typeof(string), typeof(MyTextBlock));
private readonly DependencyPropertyDescriptor myTextDescriptor;
Update: If it is any kind of clue, the problem DataGrid cells seem to be the ones that are off-screen when the addition or removal happens. I also tried OnApplyTemplate, but that didn't help.
Update2: Perhaps a better solution might be to create bindable inlines?
DataGrids virtualize their content, so if a row is not visible it will not be loaded. That being the case, have you tried also rebuilding the inlines when the Loaded event fires?

Categories