Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expose UserControl property to XAML

WPF controls have certain properties (UserControl.Resources, UserControl.CommandBindings) that can have items added to them from the XAML of a user control declaration. Example:

<UserControl ... >
  <UserControl.CommandBindings>
    ...
  </UserControl.CommandBindings>

  <UserControl.Resources>
    ...
  </UserControl.Resources>
</UserControl>

I have a new list property defined in my user control:

public partial class ArchetypeControl : UserControl {
  ...
  public List<Object> UICommands { get; set; }

I want to add items to this list like I can with resources and CommandBindings, but when I do this:

<c:ArchetypeControl.UICommands>

</c:ArchetypeControl.UICommands>

I get the error "Error 4 The attachable property 'UICommands' was not found in type 'ArchetypeControl'. "

Suggestions?

-

Given the comments, I've created a test control to show the entire code and reproduce the problem. I'm using visual studio 2010.

<UserControl x:Class="ArchetypesUI.TestControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:c="clr-namespace:ArchetypesUI"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">

    <c:TestControl.TestObject>

    </c:TestControl.TestObject>

    <Grid>

    </Grid>
</UserControl>

-

namespace ArchetypesUI
{
    /// <summary>
    /// Interaction logic for TestControl.xaml
    /// </summary>
    public partial class TestControl : UserControl
    {
        public Object TestObject { get; set; }

        public TestControl()
        {
            InitializeComponent();
        }
    }
}

Now the error I get is "Error 2 The attached property 'TestControl.TestObject' is not defined on 'UserControl' or one of its base classes."

like image 550
Jared Avatar asked Feb 27 '23 10:02

Jared


2 Answers

Take a look at your XAML:

<UserControl>
 ^^^^^^^^^^^
    <c:TestControl.TestObject>
       ^^^^^^^^^^^
    </c:TestControl.TestObject>
</UserControl>

Here, you are declaring a UserControl, and then trying to set a TestControl property on it. Since UserControl doesn't have the TestControl.TestObject property, WPF that it can't set that property on the UserControl object. You may say, "But I'm declaring a UserControl of type TestControl. My UserControl is a TestControl!" But that's not quite the case. The above declaration is declaring the TestControl class: it's not creating an instance of TestControl, so it can't have instance properties set on it.

Rather, the TestObject property is there for users of TestControl to set on individual instances of TestControl:

<local:TestControl>
  <local:TestControl.TestObject>  <!-- Now it will work -->
  </local:TestControl.TestObject>
</local:TestControl>

If you want to set a default / initial value for the TestObject property, then you can do so either in the TestControl constructor, or (if TestObject is a dependency property) through the TestControl default style (though this is more for custom controls than for user controls).

like image 91
itowlson Avatar answered Mar 03 '23 05:03

itowlson


I'm not quite able to recreate your issue... the case I've created seems to work. I did have to initialize the list in the constructor.

However, from your example I wonder a more appropriate place for your list source would be on a ViewModel object of some sort. If you're exposing commands, having an IEnumerable of some sort of a ICommand wrapper which also encapsulates the display elements you need (e.g. Caption, Icon URI, etc).

ViewModels are certainly not a panacea, but in this case I think it would let you put all the knowledge of the commands you want to use in the same place (e.g. which are available and what they do).

like image 30
Ben Von Handorf Avatar answered Mar 03 '23 07:03

Ben Von Handorf