WPF DataTemplate resets some dependency properties when unloaded - c#

I have a DataTemplate consisting of a media element control that is derived from MediaElementBase from the WPF Media Kit library. The MediaElementBase class provides the two properties, LoadedBehavior and UnloadedBehavior that allow the user to specify what happens when the element is loaded/unloaded.
I am finding that when using this in a DataTemplate (as follows), these properties get reset to their default value when the template is unloaded, but before the Unloaded event is called, meaning only the default UnloadedBehavior will ever execute:
<DataTemplate DataType="{x:Type Channels:AnalogChannel}">
<Controls:AnalogTvGraphFileElement
LoadedBehavior="Play"
UnloadedBehavior="Stop"
Channel="{Binding}" />
</DataTemplate>
This doesn't occur when the control is simply an element on a page and Unloaded occurs through a normal navigate-away event.
Debugging the DependencyPropertyChanged EventHandler reveals that an internal method System.Windows.StyleHelper.InvalidatePropertiesOnTemplateNode (in PresentationFramework.dll) checks if a DependencyProperty is potentially inherited, and if it isn't, invalidates it. Sure enough, changing the property metadata for the LoadedBehavior / UnloadedBehavior to add FrameworkPropertyMetadataOptions.Inherits stops this property from being reset when the template changes.
Does anyone know why this occurs? I can add the Inherits flag as a workaround, as this element has no child elements that would be affected by this, but I'd like to know why / whether it is the right thing to do.
If you're after more information about what I'm doing and why I'm changing DataTemplates you can view this question for a description.

Rather than putting this element directly into the template, have you tried creating a user control that contains your AnalogTvGraphFileElement element, and then using that user control in your template?
If the problem here is being caused by the template systems going and unsetting properties that it set in the first place, moving your element into a user control should help, because the properties would no longer be being set from the template.
As for why you're seeing the behaviour in the first place, as far as I can tell the relative ordering of the Unloaded event and the loss of properties set via the template is not documented, so you shouldn't depend on any particular ordering. The fact that you will lose the property values is documented. (Or at least, it's implicit from the docs.) The WPF property system treats local values in a template as being a different sort of thing than normal local values outside of a template. See this MSDN page on dependency property precedence - 4b indicates that local property sets in the template are not the same thing as local properties. (It seems odd to make a distinction, but it should be possible to set property values from both sources by setting them in the template - type 4b - and then at runtime, going and finding the element in a particular instance of the template and setting its local value from code - type 3. And for that scenario you really would want type 3 local values to have higher precedence than type 4b local values.)
Weird as that seems, it might make more sense when you consider that a single template may be providing values for multiple instances. You've got just one local property setter that could affect any number of elements. (This means that a simple mental model of a template as being a factory that builds a visual tree and sets properties on that tree would be wrong. It's a factory that builds a visual tree, but it doesn't set properties. The property system simply ensures that in the absence of any higher-precedence property value sources, the elements in a visual tree that's the template for something will pick up values from setters in the template.)
That page does just about tell you that type 4b properties will disappear once the template ceases to be active - the template is no longer the template for the templated parent, and so any local values provided by that template no longer qualify as candidate values of type 4b (or any other type for that matter) for the property. In short, once the template ceases to be the template for something, it ceases to have any business providing values for that thing or anything in it. This implies that the visual tree for an instance of a template enters a weird limbo state in which it is no longer the template for anything but hasn't yet unloaded.
Of course, it doesn't seem useful for the template to stop providing values before the relevant visual tree has finished unloading, but perhaps properties whose value is only significant when an element unloads are a bit of a weird case, and not one that was specifically designed for. Thinking about it, they probably shouldn't ever be dependency properties - almost all the features that make DPs useful don't really mean much once you're unloaded from the visual tree. So arguably, it's a bug in the Media Kit that UnloadedBehaviour is a DP in the first place.
It's inconsistent that inherited properties (type 10) shut down later than local template property sets (type 4b), but then I'm not sure it's reasonable to expect any particular order here. It's not obvious that the documentation implies one order or the other, so either order is correct...and WPF appears to make use of that by picking one order in one scenario and the other in another scenario.

Related

c# wpf, is xaml nested property/controls inheritance possible?

<Grid Name="mainSceneGrid" Grid.Row="1" Background="#FF075035">
<Grid Name="navigationGrid">
......
</Grid>
</Grid>
Normally from the code behind we would directly call navigationGrid.xxx which is very simple and effective. However when we start to get alot of dependent and nested grids for example it would be awesome to instead be able to call a property like this:
mainSceneGrid.navigationGrid.xxx
So we get some form of structured code and easier intellisense to work with, is this possible with xaml?
You misunderstand what the names of UI elements are.
For XAML, firstly, it is registering a name in the visual tree of elements using the FrameworkElement.RegisterName (String, Object) method and then searching in this tree using the FrameworkElement.FindName (String) method.
This is mainly used for bindings of type ElementName.
These names must be unique within the scope of the names.
Secondly, the x: Name Directive creates, in addition to the name in the visual tree, a field in the "* .g.i.cs" file.
This file is the XAML reflection generated by Studio Designer.
You can view it if you place the cursor on the call to the InitializeComponent () method and press F12.
It automatically changes when you change the XAML code.
And when you reference the name of an element in Code Behind, you are not actually referring to the name in XAML, but to a field in that file.
And if you know even a little about Sharpe, then you understand that you cannot create fields with names like name1.name2.
As for your problem, its origins are that you are not working with WPF in a way that is typical for WPF.
WPF is designed around the concept of UI elements getting values ​​for their properties on their own through bindings.
Therefore, using WPF element names other than ElementName bindings is a sign of incorrect implementation.
Such incorrect implementation methods are fraught with the occurrence of specific tasks, code bloat, and a high probability of bugs and errors.
I advise you, especially at the initial stage of training, in general, do not even open the "* .xaml.cs" files.
There should be nothing in the Code Behind other than calling the InitializeComponent () method.
When you learn the basics of WPF (layout, bindings, data context, styles, templates, triggers, converters), then you will be able to consciously decide on the use of Code Behind.
But in practice, there are almost no such tasks where it is really needed.

Need to extend DataGridNumericalColumn for Telerik UWP

I have a need to create a custom version of the UWP DataGridNumericalColumn that allows customization of the RadNumericBox properties (ValueFormat, ButtonsVisibility, SmallChange, LargeChange, Value) as well as the ability to edit the value as cents (199) without decimal place while editing, but display as normal dollars with decimal cents (1.99) while not editing. I've tried two different approaches to extend existing controls, neither of which I can seem to get to work fully for me.
1) Tried deriving from DataGridNumericalColumn - impossible due to inaccessible internal members down the line, even with full source code from GitHub available.
2) Tried deriving from DataGridTemplateColumn - somewhat workable for initial display, but everything relating to inline edit mode vs display mode and validation message display on the cell seems beyond reach (not override-able) and I can't seem to use CellContentTemplateSelector to choose between the inline Edit mode RadNumericBox display and the normal TextBlock display because I can't seem to detect when Edit mode is applied to the cell.
It is starting to seem like the only way I can achieve what I need is to fork the GitHub code-base so I can derive from DataGridNumericalColumn with access to internal code.
What approach could I take to achieve my desired customizations?
(I am using Telerik UI For Universal Windows Platform, version 2017.1.301.45, at the time of this writing.)
I eventually worked out workarounds that let me get past the main difficulties in extending this functionality using the derive from DataGridTemplateColumn approach. Here are the updates and customizations I made - they are described mostly at the conceptual level, but it should be enough for others to duplicate this sort of customization for themselves.
UPDATE1:
An update as I've been working on this:
Continuing along with the approach of deriving from DataGridTemplateColumn, I found that I can successfully change my displayed markup for edit mode vs display mode by creating custom commands for editing operations in the grid (CustomBeginEditCommand, CustomCancelEditCommand, and CustomCommitEditCommand very similar to the ones in http://docs.telerik.com/devtools/universal-windows-platform/controls/raddatagrid/features/commands/editing-commands/datagrid-editingcommands-begineditcommand ) along with an interface IItemAwareOfEditMode, applied to the ViewModel items for the Grid's data, that has a single bool property IsInEditMode that I set to true or false appropriately in the custom commands, which is then used in a custom DataTemplateSelector to decide when to apply my edit markup vs my display markup. This uses (DataTemplate)XamlReader.LoadWithInitialTemplateValidation(editControlMarkup) for translating dynamically created markup strings to DataTemplates. For my implementation, I create the markup in a PropertyChangedCallback for my custom column's PropertyNameProperty dependency property.
However, I still am having issues with validation and displaying the validation messages, and reverting values when the user cancels edit. I have the ViewModel for the grid row items implemented such that they derive from ValidateViewModelBase, and so they add/remove errors appropriately according to the documentation on validation at http://docs.telerik.com/devtools/universal-windows-platform/controls/raddatagrid/features/validation . If I use the DataGridNumericalColumn (not customized) with the same data, the validation messages do appear pointing to the cell when the data is invalid, but with my custom column, HasErrors is true on the items, but the validation messages don't appear. Looking at the validation code in https://github.com/telerik/UI-For-UWP/blob/master/Controls/Grid/Grid.UWP/View/Columns/TypedColumns/DataGridTypedColumn.cs in the CreateEditorContainer function, it seems there is an EditRowHostPanel and ValidationControl involved along with the editor content, but I don't have access to pieces needed to implement the container exactly as is done there.
What can I do to get the validation messages to appear as they do in the DataGridNumericalColumn?
Also, what can I do to make cancelling an edit (clicking the blue X for the row when in edit mode) actually revert my custom column's value to what it was prior to entering edit mode?
UPDATE2:
Another update as I've been working on this:
Continuing along with the approach of deriving from DataGridTemplateColumn, I've managed to successfully display validation messages for the edit mode by including a ValidationControl in the edit mode template markup, which references the RadNumericBox from the template (by Name) using the ControlPeer property, and giving its DataItem property a value of "{Binding}", and appropriately populating its PropertyName.
This is getting close to what I need, but it seems that my CustomCancelEditCommand, which uses
Owner.CommandService.ExecuteDefaultCommand(CommandId.CancelEdit, context);
, does not appropriately update the display of the cell to its previous value. It correctly doesn't call the CustomCommitEditCommand when the inline row edit is cancelled; however, it displays as the modified value (not reverted to the value prior to edit). Even if you edit again, the value remains as the modified value when displayed in the grid.
I see that in https://github.com/telerik/UI-For-UWP/blob/master/Controls/Grid/Grid.UWP/View/Services/Commands/Editing/CancelEditCommand.cs in the Execute method, that it executes its base implementation, followed by
Owner.editService.CancelEdit(context.TriggerAction)
, which I don't understand (RadDataGrid does not contain a definition for editService and I can't derive from that CancelEditCommand class because it is internal).
What can I do to make cancelling an edit (clicking the blue X for the row when in edit mode) actually revert my custom column's value to what it was prior to entering edit mode?
UPDATE3:
I have finally managed an EXTENSIVE workaround that does revert my custom columns' value on cancel.
My workaround for the cancel functionality involved:
1) Created a CustomRadDataGrid, which derives from RadDataGrid.
2) Gave my CustomRadDataGrid class a CustomEditingService property, which is a CustomEditingService, which is copied and modified code from EditingService (mostly commenting out unneeded parts, but also changing InitializeEditOperation's implementation and changing CancelEdit to have an out parameter of the operation's OriginalValues dictionary), and which derives from CustomServiceBase<RadDataGrid>, which is copied and modified code from ServiceBase (changed IsOperational to return Owner.DataContext != null), which derives from CustomAttachableObject<T> where T : RadControl, which is copied code from AttachableObject.
3) Added GetActualValueForInstance function and SetActualValueForInstance method to my custom column, which uses reflection to get/set the data row instance's value for this column (based on using my PropertyName Dependency Property's value), and made the InitializeEditOperation of my CustomEditingService just save original values of my custom columns, and made the CancelEdit of my CustomEditingService return that dictionary of original values in an out variable.
4) Made my CustomBeginEditCommand call BeginEdit on the grid's CustomEditingService after calling Owner.CommandService.ExecuteDefaultCommand(CommandId.BeginEdit, context) - that allows my custom column original values to be stored.
5) Made my CustomCommitEditCommand call CommitEdit on the grid's CustomEditingService after calling Owner.CommandService.ExecuteDefaultCommand(CommandId.CommitEdit, context) - that allows my custom editing service to properly track its editing state.
6) Made my CustomCancelEditCommand call CancelEdit on the grid's CustomEditingService AND for each original value dictionary item, use the Key (column, as my custom column) SetActualValueForInstance passing in context.CellInfo.Item and the Value (previously stored original value), BEFORE calling Owner.CommandService.ExecuteDefaultCommand(CommandId.CancelEdit, context) - that restores my custom column original values prior to the standard cancel actions occurring.
Done! Whew... It seems that this library needs a lot of changes to allow for better extend-ability. This has been logged as a feature request at Telerik according to a reply to my support ticket with them on this subject.
I think other people will want to be able to extend Telerik's various DataGridColumn controls too, so I shared my struggle and (eventually) successful customization here.

XAML: Under the Hood

Well, i must admit, still sometimes XAML seems a bit mysterious to me. The thing is, i always liked to debug through the C# code (setting lots of breakpoints in them) to get the idea of "what is happening" and "how is it happening". But with declarative XAML syntax that's not an option. I think you'll agree that to work with XAML, or to be precise, to work with/understand some existing XAML code you got to "already know" how things work with XAML declaration. There is just no way you can know/learn things investigating the execution of your application code. So i'm more than interested to take a look through XAML inside-out, as detailed as possible. I'm NOT talking about "learning" XAML, I know the basic stuff. May be i can provide some examples to clarify the sort of things i'm looking for -
Compared to C# code how an object gets instantiated when we use them in XMAL? Are they stored in managed heap? Same way as C# code-instantiated objects?
How the properties get set while using Mark-Up Extension syntax for Data/Command Binding?
When any property of an INotifyPropertyChanged type gets updated, how the Binding instatnce inside the XAML syntax updates the itself? How exactly it gets notified it at the first place, & by whom?
A viewmodel can be set as the DataContext of a view at runtime by defining Typed DataTemplate, like -
<DataTemplate DataType="{x:Type viewmodels:AccountsViewModel}">
<views:Accounts/>
</DataTemplate>
How does it happen actually? What are the rules for setting DataContext other than searching for the DataContext property upward the logical tree?
How the whole template things (DataTemplate, ControlTemplate & ItemsPanelTemplate) are treated/resolved at run time.
etc. etc. etc.
So if you are good/experienced/expert in XAML what would you suggest (links, articles, blogposts, books whatever) as reference that helps getting clear & deeper understanding about how XAML works "under-the-hood"? Thanks in advance.
Most can be explained by don't thinking of XAML as a real programming language, more like a declarative language. Everything you do in xaml, can be made in C# aswell, and in fact this is whats happening.
Compared to C# code how an object gets instantiated when we use them
in XMAL? Are they stored in managed heap? Same way as C#
code-instantiated objects?
Yes because they are just c# objects. Most resources are stored in a hibernated state, i rememberd the word inflated somewhere. Converter or other "direct" c# objects are created when they are needed. Important here is that these resources are usually shared, so they will get created only once.
How the properties get set while using Mark-Up Extension syntax for Data/Command Binding?
This again depends on where you use the markup extension. In a Style? In a Template? In a instanced user control like a window? Usually they are evaluated when you actually need them. It wouldn't make sense to evaluate them, when the inflated style is stored in the actual resource dictionary. They get evaluated when you actually use the style on an object.
When any property of an INotifyPropertyChanged type gets updated, how
the Binding instatnce inside the XAML syntax updates the itself? How
exactly it gets notified it at the first place, & by whom?
By the binding engine. WPF checks if your DataContext inherits the INotifyPropertyChanged interface, attaches to the event provided by the interface and listens to any changes. If such an event is raised, the binding engine will just call the getter again.
How does it happen actually? What are the rules for setting DataContext
other than searching for the DataContext property upward
the logical tree?
In short: None other. Datacontext is simply an inherited attached property. If you don't re set it on a child control, it will take the value the parent has until it reached the root. The only exception to this are ContentControls and ContentPresenter they will not inherit the DataContext but will change them depending on the content. So these controls always have by default the Content as their DataContext.
How the whole template things (DataTemplate, ControlTemplate & ItemsPanelTemplate) are treated/resolved at run time.
Simply spoken: Everytime WPF finds a non ui object, it tries to find a DataTemplate for the given type. In an ItemsControl for example: You can bind a list of MyClass; unless you provide an explicit DataTemplate or DataTemplateSelector it will search the resource tree upwards for an implicit style. Again remember that this already does not happen in XAML, but on the C# objects that was generated out of the xaml.
And is it by any means possible (at present or near future) to debug
through XAML code?
How do you think you can debug something that is not executed, but evaluated on compile time?
Please don't take this as 100% correct. Over the Years this is what i gathered of informations about XAML and the usage. If you have any corrections or find something that is clearly wrong. Please tell me, we are all here to learn and i always learn new things about the stuff i use :)

Visual Studio Designer is always trying to change my control

I have a somewhat complex UserControl, and Visual Studio 2008 is giving me a rather harmless annoyance when working with it. Every single time I open the control with the Designer, it decides to immediately change some of the harmless values set by the designer - namely the initialization of Size properties. If I save those changes, close, and reopen, it almost invariably ends up deciding another component of my control needs its initial size changed, ad infinitum. Luckily these changes are harmless since I'm using automatic sizing everywhere, but this is quite annoying to work with. I haven't the foggiest on where to start figuring out what's going wrong, my only thought right now is that the Designer is assigning the results of auto-sizing back into the initial size fields every time I open the control. Any ideas on causes/fixes?
Edit: Also, I am using Application Settings to save sizes of certain resizable child components across runs of the application, but I really hope the Designer is smart enough to understand that it should only ever be using the defaults.
Maybe it can help:
I noticed that FormDesigner (no WPF, no Web etc) has a strange behaviour if you insert one custom UserControl.
There is a random change of other controls (GroupBox, EditBox, ComboBox) size (to me happened with width).
The controls choosen to resize seems to be random, but across restarting of vs2010 it is always the same. If deleted and reinserted, the designer chooses a different control do randomly resize...
I changed the property AutoScaleMode of my UserControl from "Font" to "Inherit" and it did not happen again.
You're right, the designer often tries to add default values to properties.
Add this on top of the property declaration:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
That will tell the designer to ignore this property.
I have somewhat similar problem. I am using Infragistics GroupBox on a user control which I inherited and now want to change its look and feel in the derived class. I have made it protected in base class -- so it does allow me changing properties in derived class. But it does not save it. Every time I open it -- I get same old values of base class back.
Any idea?
Edit: I figured it out.
Trying various value for one of the above given answers.
Using [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] instead of [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] generates code for changed properties - and things work as desired.
Try overriding the DefaultSize property of your control.
From MSDN:
The DefaultSize property represents the Size of the control when it is initially created.

How is the Parent property of a FrameworkElement set in Silverlight?

I have written a custom Silverlight control based on Control. I have two DependencyProperties called Top and Bottom which both hold child controls for a specific layout display. I then use a ControlTemplate to arrange these two controls into a grid, placing one on the 0 row and the other on the 1 row. The problem I have is that I cannot seem to figure out how to get each child control's Parent property to point to my custom control. When I inspect each control at run-time, the Parent property of each is null.
This is a simple example, but I think you can see the general problem. I have a number of more complex controls that all share this problem. I know there is some magic I am missing. If a ContentControl's Content property is set to some child it is somehow setting that child's parent to itself.
Edit: A little more info
In WPF, one might use functions like AddVisualChild(), RemoveVisualChild(), AddLogicalChild(), RemoveLogicChild() to manage parent/child relationships, but these functions are not available in Silverlight.
After quite a bit of research I believe that this is not possible. I was able to recurse through the Visual Tree instead of the Logic Tree using the VisualTreeHelper to accomplish my ultimate goal.
The Parent property cannot be arbitrary, it reflects the real parent of the control for use when rendering.
From MSDN:
Parent may be a null reference (Nothing in Visual Basic) in cases where an element was instantiated, but is not attached to any logical tree that eventually connects to the page level root element, or the application object.
...
Changing an element's parent is typically only done through manipulation of collections, by using dedicated add or remove methods, or through setting content properties of elements.

Categories

Resources