rice
rice

Reputation: 1063

Going from static WPF XAML to programmatically added controls broke DevEx LayoutControl.ReadFromXML

EDIT: Figured it out, problem was not creating the name scope and registering the name. Now working, will provide an answer below.

ORIGINAL QUESTION:

I think I'm doing something wrong when programatically adding controls to my WPF screen.

Been banging on this prototype of a drag-and-drop configurable UI that lets you save and load layouts using the WPF DevEx LayoutControl.

It was working, but I broke it when I went from using a static WPF screen made in Visual Studio to a screen that had FrameworkElements added programatically.

Here is a simple test of adding an element to a LayoutControl:

    public static void InstallItemsInLayoutControl(LayoutControl layoutControl)
    {
        // FAKE TEST VERSION
        Button btn = new Button();
        btn.Name = "TESTBUTTON";
        btn.Content = "Test Me";
        btn.Height = 23;
        btn.Width = 100;
        btn.HorizontalAlignment = HorizontalAlignment.Left;
        btn.VerticalAlignment = VerticalAlignment.Top;
        btn.Width = 100;

        layoutControl.Children.Add(btn);
    }

For the results below, this is the only item added to an otherwise empty screen.

When I load this screen up and enable the drag-and-drop editing mode, sure enough that button is present. It's not visible, but I can pull it into the screen from the available items, move it around, and save the layout to XML that looks good. For example, here is a trivial layout of nothing on the screen but this button, at the top level:

  <?xml version="1.0" encoding="utf-8" ?> 
- <LayoutControl Orientation="Vertical">
  <Element ID="TESTBUTTON" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Height="23" /> 
  <AvailableItems /> 
  </LayoutControl

If I remove the Button from the screen and save, the XML correctly looks different:

  <?xml version="1.0" encoding="utf-8" ?> 
- <LayoutControl Orientation="Vertical">
- <AvailableItems>
  <Element ID="TESTBUTTON" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Height="23" /> 
  </AvailableItems>
  </LayoutControl>

(note the button is no longer in the layout, but rather in the available items)

I have looked at the DevEx code, and the take the ID from the XML element, and they pass it to FrameworkElement.FindName(string), which documentation says looks at the Name property.

There seems to be a match between the ID and the Name property of my runtime object.

Looking in the WPF Tree Visualizer, I see in the visual tree that the hierarchy looks like this when I first show this screen:

AdornerDecorator->AvalonAdapter->LayoutControl->ItemsContainer->TESTBUTTON

I am not sure what this indirection of the ItemsContainer is, but I suspect it is the "available items" container, which is where stuff not on the screen but available to be customized onto it hangs out.

The visualizer shows that the Name property at runtime is indeed TESTBUTTON.

This exact scenario was working with the items not being added programatically, and I guess my next step is to put some design-time elements in there as well.

But I'm very new to WPF, is there anything obvious that I'm doing wrong in how I add my button that might expalin this?

Thanks.

Upvotes: 0

Views: 1470

Answers (1)

rice
rice

Reputation: 1063

The name lookup was indeed failing, because I didn't include a name scope and then register the name.

Correct implementation was to:

  1. Create a name scope for my LayoutControl at instanciation time:

    NameScope.SetNameScope(layoutControl, new NameScope());
    
  2. Register my button's name:

    public static void InstallItemsInLayoutControl(LayoutControl layoutControl)
    {
        // FAKE TEST VERSION
        Button btn = new Button();
        btn.Name = "TESTBUTTON";
        btn.Content = "Test Me";
        btn.Height = 23;
        btn.Width = 100;
        btn.HorizontalAlignment = HorizontalAlignment.Left;
        btn.VerticalAlignment = VerticalAlignment.Top;
        btn.Width = 100;
        layoutControl.RegisterName(btn.Name, btn);  // THIS LINE WAS MISSING
        layoutControl.Children.Add(btn);
    }
    

Upvotes: 1

Related Questions