When is DataTemplate used in ContentControl and ItemsControl? - wpf

I have WPF controls that I created that inherits the FrameworkElement class. One of them is done like this:
[ContentProperty("Children")]
public class ItemsElement : FrameworkElement
{
public ItemsElement()
{
Children = new UIElementCollection(this, this);
}
public UIElementCollection Children { get; private set; }
protected override int VisualChildrenCount
{
get
{
if (Children != null)
{
return Children.Count;
}
return 0;
}
}
protected override Visual GetVisualChild(int index)
{
return Children[index];
}
protected override IEnumerator LogicalChildren
{
get
{
if (Children != null)
{
return Children.GetEnumerator();
}
return EmptyEnumerator.Instance;
}
}
protected override Size MeasureOverride(Size availableSize)
{
return availableSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
return finalSize;
}
}
What I want with this code is to have elements that are not 2D-UI elements. I overrided the Measure and Arrange methods so that it does not compute Sizes of the Children. This is a behavior that I want because these computations are heavy and useless.
The problem is if I put ContentControls and ItemsControls in the Children of my ItemsElement class (through XAML), these controls do not seem to generate their Visual children from Content/ItemsSource with the DataTemplate I defined for them in the Resources.
It does work if I add this code at the beginning of the MeasureOverride method:
foreach (UIElement child in Children)
{
child.Measure(availableSize);
}
I can't understand why...
Do you have any idea?
Thanks!

The question really is why are you extending a class who's behaviour you don't want? The answer is too long for here, but you can read it in full in the Control Authoring Overview page on MSDN. But to answer in short, I can only assume that you are extending the wrong class. Maybe you don't even need to extend any class.
In WPF, there are many alternatives to writing a new control. Often the only real need to do that is to perform some special custom drawing for a unique control that cannot be composed by other means. As the linked page says, WPF enables you to customize existing controls by using its rich content model, styles, templates, and triggers. Further on it continues:
Controls that derive from UserControl or Control rely upon composing existing elements. For many scenarios, this is an acceptable solution, because any object that inherits from FrameworkElement can be in a ControlTemplate. However, there are times when a control's appearance requires more than the functionality of simple element composition. For these scenarios, basing a component on FrameworkElement is the right choice.
Finally, the MeasureOverride and ArrangeOverride methods are required to be implemented correctly in order for the FrameworkElement to render its items. You just need to look at MSDN to find this basic information out. From the FrameworkElement.MeasureOverride Method page:
Your implementation should do the following:
1.Iterate your element's particular collection of children that are part of layout, call Measure on each child element.
And from the FrameworkElement.ArrangeOverride Method page:
Parent elements should call Arrange on each child, otherwise the child elements will not be rendered.
Please read the linked pages for further information and in future, please look in MSDN for information regarding your problem control, method, event, etc. before asking questions here.
UPDATE >>>
In response to your somewhat offensive comment, I can see you do have a valid reason to extend the FrameworkElement class, but that doesn't change anything. You say that you've read all the pages that I showed you, but for some reason, you haven't followed the clear instructions on what you have to do to render the items correctly.
There's no need to answer your question regarding the DataTemplate and when exactly it is rendered because it is irrelevant to your problem. Your problem is caused simply because you have not followed the instructions found on MSDN in your class. That's it. One thing that you can check to see if this is correct or not is this.
If you see the full name of the type of objects in the collection when you run your application, then you have a problem with your DataTemplate not being applied. However, if you see no items, then you have a problem with your rendering and that's simply because you haven't implemented those two essential methods correctly.

Related

Is it possible to create a control once and have it generated everytime it is needed?

Say for example you have a stackpanel that you would like to programmatically add buttons to.
The code behind for the button generation and addition to the stackpanel is:
Button button = new Button();
button.Content = "Button";
button.HorizontalAlignment = HorizontalAlignment.Left;
button.Name = "Button" + i
stackPanel1.Children.Add(button);
My question is - Is it possible to generate the button once and have it as some kind of template that can be added to the stackpanel whenever it is needed without going through the generation code again?
In WPF each UIElement can only be the logical child of one control at any given time, see WPF Error: Specified element is already the logical child of another element. Disconnect it first, so no you can't use the same button and add it to another control later on, unless you're sure you've gotten rid of that stackpanel
But, you can do recycling. See Optimizing Performance: Controls. especially if you are willing to override MeasureOverride and ArrangeOverride of you stackpanel
I've actually written such a recycler because I had grids with many controls, and I wanted to implement some kind of virtualizing grid. These are the main methods of my class:
internal class Recycler
{
private UIElementCollection _children;
public Recycler(UIElementCollection children)
{
_children = children;
//You need this because you're not going to remove any
//UIElement from your control without calling this class
}
public void Recycle(UIElement uie)
{
//Keep this element for recycling, remove it from Children
}
public UIElement GiveMeAnElement(Type type)
{
//Return one of the elements you kept of this type, or
//if none remain create one using the default constructor
}
}

Can custom UIElement implement IList<UIElement> and be assigned children directly (in XAML)?

Scenario: I have a range of custom UIElements (in fact, I have replaced all the standard WPF FrameworkElements I would use with lighter, more efficient counterparts) for a custom layout system intended to only use those. They all inherit from a class called Surface (which in turn is a direct descendant of UIElement).
I am now wondering if my version of Panel (let's call it SurfacePanel) can simply implement IList<Surface> and allow child Surface elements to be added directly to it, rather than to a Children property (as with regular WPF panels), in XAML.
To illustrate - in codebehind, I can do now this:
SurfacePanel.Add(child);
And from that, I would like to be able to do this in XAML:
<SurfacePanel>
<child />
</SurfacePanel>
But XAML seems to require me to have a codebehind pattern like this:
SurfacePanel.Children.Add(child)
(I don't really need these controls to support XAML to work in the runtime environment, but when testing and prototyping, I like to make my UI controls "XAML friendly" so I can benefit from the visual designer in VS (along with the property pane etc), if nothing more than as a 'preview' window).
Since my controls inherit from UIElement (and have the proper Measure/Arrange/Render overrides and so on), they function quite well when put on, say, a regular Canvasor Grid. But the VS XAML parser is not too happy about my SurfacePanel (that implements IList<Surface>) when I am adding children to it in markup. It says "Cannot add content to an object of type "SurfacePanel"".
I know that if I add a Children property of an appropriate type and add an attribute to the SurfaceCanvas class ([ContentProperty("Children")]), it will work. But since the SurfacePanel is itself a collection capable of the same thing, is there a way to make XAML 'get it'?
Edit:
I can solve the XAML 'compliance' by adding a Children property on the SurfacePanel that simply returns its inner List, but then adding and removal of elements on that directly bypasses the internal logic that wire the child elements up.
If the inner list was an ObservableCollection, I could do it the conventional way and do the wiring in a CollectionChanged event handler - but basically the whole point of integrating IList in the Panel directly is to avoid that..
Edit 2:
This "works" (but bypasses the wiring):
[ContentProperty("Children")]
public class SurfacePanel : Surface, IList<Surface>
{
private readonly List<Surface> _children = new List<Surface>();
public List<Surface> Children
{
get { return _children; }
}
}
I cannot return this because SurfacePanel is not a List<Surface>, but an IList<Surface>.
If I change the property to
public IList<Surface> Children
{
get { return this; }
}
I get an error message even with the following XAML (but not with <m:SurfacePanel/>):
<m:SurfacePanel>
</m:SurfacePanel>
The error message is
Cannot set content property 'Children' on element 'SurfacePanel'. 'Children' has incorrect access level or its assembly does not allow access.
Also implement IList and declare the Children property like this:
[ContentProperty("Children")]
public class SurfacePanel : Surface, IList, IList<Surface>
{
public IList Children
{
get { return this; }
}
...
}

How do you call identically named properties on different types which don't share an interface?

I have a DataTemplate that needs to set the IsSelected property on an ItemsControl's container (such as TreeViewItem, ListViewItem or ComboBoxItem). However, it doesn't know the type of the container until it's passed in to it. Since IsSelected isn't part of a common base class or interface, nor is it a common dependency property registered with AddOwner to the various classes (Duh, MS!!! WTF not?!!), I ended up with this mess...
if (container is TreeViewItem) {
(container as TreeViewItem).IsSelected = true;
return;
}
if (container is ListBoxItem) {
(container as ListBoxItem).IsSelected = true;
return;
}
if (container is ComboBoxItem) {
(container as ComboBoxItem).IsSelected = true;
return;
}
...which not only is verbose, but requires me to modify it if I ever use a different ItemsControl that uses different container types! Not good!
Sure I could enhance it a little by putting this logic in extension methods (damn C# for not having extension properties!!) called IsContainerSelected and SetContainerSelected and putting them on UIElement, then moving the above code inside there, but it's just making the outside neater. The inside is still a mess.
My only other thought is to use reflection and look for an IsSelected property and use that if found, but I'm always leery of doing things like that. However, since there isn't a common interface or base class, I'm not really sure I have a choice here.
For context, I'm sharing a complex data template between several different ItemsControls and the template itself has controls that can receive focus such as checkbox and textbox. However, when those controls receive focus via the mouse, the underlying container item doesn't get selected and whatever was selected before remains so.
My workaround is to use an attached behavior that utilizes the preview events to intercept the focus before it happens and set the underlying item accordingly, which works great when I've hard-coded TreeViewItem or ListBoxItem, etc., but I don't want to hard-code the type since the control shouldn't really care. So that's the part that breaks down.
Ugh!!! Why didn't MS just register the same attached property or at least create an ISelectableContainer interface?!!
I have read your answer, and it does make sense - in your case, IsSelected may obviously be part of the ViewModel, and that seems to be the best solution in your case.
But you asked for further explanation about C# dynamic features. C# 4.0 now has some dynamic functionalities, which allow us to create code that would only be possible in languages like Python, Ruby or JavaScript. This, of course, has its cost - a dynamic abuse would not only make code slower, but also more confusing - because you would lose compile-time errors and IntelliSense.
I have written a simple example so you may understand it better:
public class ClassOne
{
public int SameProperty { get; set; }
}
public class ClassTwo
{
public int SameProperty { get; set; }
}
public class ClassThree
{
public string SameProperty { get; set; }
}
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
dynamic wrapper = new ClassOne();
wrapper.SameProperty = 5;
wrapper = new ClassTwo();
wrapper.SameProperty = 15;
wrapper = new ClassThree();
wrapper.SameProperty = "Now it is a string!";
// And now a run-time error...
wrapper.AnotherProperty = "And this won't work...";
}
}
As you can see, wrapper has no definite type whatsoever - a dynamic reference will allow any kind of method or property invocation, since the actual binding will only be made during run-time, not compile-time.
Of course, this example is very naive, but sometimes dynamic code may be useful - it is a good option to avoid explicit reflection, or to avoid long if...else statements based on type (like your snippet above).
I'm not sure that I fully understand your problem, but you could try adding an IsSelected boolean to your model and then binding that property against the Item control it's contained in. That way, you just have to worry about setting that property in the model, regardless of the container.
Per #mdm20's answer, he suggested modifying the ViewModel, which is of course normally what you want to do. However this is a purely view-related issue (keyboard navigation-related) and isn't reflected in the ViewModel at all, nor in this case should it be.
But that gave me an idea! Since I'm using a custom control to render the item in whichever items control (via its data template) it's being added to, that control naturally does have multiple instances (all of which are pointing to the same ViewModel instance), which is what I want!
Therefore, rather than adding the IsSelected to the ViewModel, I added it to the user control itself, then I just bind to that within the data template for the respective ItemsControl which I do know about. I can then set the IsSelected property in the code-behind for the user control as needed (i.e. during the preview mouse events, etc.) and the underlying ItemsControl responds appropriately! Works great and keeps the ViewModel clean since neither the model, nor the viewmodel need to know about it. The IsSelected remains purely in the UI which is where in this particular case it should be!

WPF element that dynamically creates (encapsulated) children at runtime

I want to create a WPF element that, at runtime, is in full control of its child elements -- adding and removing child UI when its properties change. Something a bit like what ItemsControl does when you modify its ItemsSource property, though in my case there'll only be one child.
This will be a view container for MVVM -- when you give it a Model or a ViewModel, it will magically create the correct View and wire everything up. There's no need for my view container to be templatable (since it creates user-defined views, which are UserControls and have their own templates), and I'd prefer that it encapsulate as much as possible. I could probably do this easily by descending from something like Grid, and adding child controls when my own properties change; but Grid publicly exposes its collection of child elements and lets anyone add and remove stuff.
Which WPF class should I descend from for maximum encapsulation, and how do I add child elements to it at runtime?
Based on my understanding of the docs, I tried using FrameworkElement and AddVisualChild, just to see if I could create child controls at runtime. I'm not clear on whether the AddLogicalChild is necessary, but I put it in just in case:
public class ViewContainer : FrameworkElement {
private TextBlock _child;
public ViewContainer() {
_child = new TextBlock { Text = "ViewContainer" };
AddLogicalChild(_child);
AddVisualChild(_child);
InvalidateMeasure();
}
public object Content { get; set; }
protected override Size ArrangeOverride(Size finalSize) {
_child.Arrange(new Rect(finalSize));
return finalSize;
}
protected override Size MeasureOverride(Size availableSize) {
_child.Measure(availableSize);
return _child.DesiredSize;
}
}
When I put a ViewContainer into a Window, and run this, I expect to see a TextBlock saying "ViewContainer". But instead, I just see a blank window. So obviously I'm missing something.
How can I fix the above code so that the "child" control does appear at runtime, but isn't exposed for others to mess with (any more than can be avoided)?
To answer your specific question, you'll also need to override GetVisualChild and VisualChildrenCount properties to enable your child element to be displayed.
Have you thought about taking advantage of WPF's support for implicit DataTemplates?
The way I have handled a requirement similar to yours is by using a ContentControl. I bind the Content property to my ViewModel. I then make sure that in Resource Dictionaries referenced somewhere in the tree above the ContentControl I have DataTemplates defined for all the types of ViewModels that might be assigned to the Content Property.
This way WPF takes care of wiring up the correct view for my ViewModel.

HowTo write a recursive Custom Control in WinForms .NET

I am attempting to write a 'User Control' in WinForms .NET (not ASP.NET). The control is relatively simple. It will contain a label, a button, and a DataGridView.
However, the control needs to be able to instantiate itself, i.e. when the user clicks the button (of the parent control) at least 1 nested (children) control of the same type will be displayed underneath (kind of like a Tree)
I am having no success writing such a recursive control using a 'User Control'. A StackOverflow Exception occurs when instantiating MyControl within it's own constructor.
Therefore, I am leaning towards using a 'Custom Control', hoping it can handle the instantiation of itself (maybe in the Paint event??). Alot more work has to go into a Custom Control however, so I don't want to go down this path if it's going to take forever. I am on a tight deadline.
Anybody done this using a Custom Control or have any solid ideas on how to create a recursive control?
By the way, this control would be used in a fairly finite number of recursive combinations, so maybe it would be better to create a separate control for each parent/children scenario? I am thinking that would result in at least 10 separate user controls.
thanks for your help
UPDATE (here is my initial attempt at a stop condition per your feedback, but this is still causing children to be created indefinitely) :
public partial class CustomX : UserControl
{
private IList _children = new List();
public CustomX()
{
InitializeComponent();
Recurse(0);
}
private void Recurse(int childCount)
{
if (childCount
The problem is probably that the child control also instantiates a child control. There has to be a stop condition or controls will be generated until the stack overflows.
This should work:
public partial class CustomX : UserControl
{
private IList _children = new List();
public CustomX(int depth)
{
InitializeComponent();
if(depth > 0)
{
CustomX child = new CustomX(depth-1);
this.Controls.Add(child)
}
}
}
You should have no problem doing this with a user control. It is more likely an issue with not terminating the recursion properly. It (might) be more readable to perform the control creation in just the topmost parent control rather than delegating that task into each constructor.
Can you post the code you have in your constructor?

Resources