Bob Moore
Bob Moore

Reputation: 6894

WP8 xaml causes "element is already child of another element"

I am following MSDN examples to add a settings page to my first Windows Phone 8 application (warning - I am completely new to XAML, I'm a C++ guy).

The xaml looks like this:

<phone:PhoneApplicationPage
x:Class="PicoSDU.AppSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PicoSDU"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">


<phone:PhoneApplicationPage.Resources>
   <local:AppSettings x:Key="PicoSettings"></local:AppSettings>
</phone:PhoneApplicationPage.Resources>


<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <!--TitlePanel contains the name of the application and page title-->
    <StackPanel Grid.Row="0" Margin="12,17,0,28">
        <TextBlock Text="PicoSDU" Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock Text="Settings" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>

    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">


     <StackPanel Margin="30,0,0,0">
        <TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtIpAddress" Text="IP Address" VerticalAlignment="Top" Width="169" />
        <TextBox Name="tbIpAddress"  HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"  
                 Text="{Binding Source={StaticResource PicoSettings}, Path=IpSetting, Mode=TwoWay}"/>
        <TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtPort" Text="Port Number" VerticalAlignment="Top" Width="169" />
        <TextBox Name="tbPort"  HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"  
                 Text="{Binding Source={StaticResource PicoSettings}, Path=PortSetting, Mode=TwoWay}"/>
        <TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtSysId" Text="System ID" VerticalAlignment="Top" Width="169" />
        <TextBox Name="tbSysId"  HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"  
                 Text="{Binding Source={StaticResource PicoSettings}, Path=SysIdSetting, Mode=TwoWay}"/>
        <TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtWsId" Text="Station ID" VerticalAlignment="Top" Width="169" />
        <TextBox Name="tbWsId"  HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"  
                 Text="{Binding Source={StaticResource PicoSettings}, Path=WsIdSetting, Mode=TwoWay}"/>
     </StackPanel>

  </Grid>
</Grid>

</phone:PhoneApplicationPage>

So, pretty simple. four text boxes. It rendered perfectly OK until I added the resource clause

<phone:PhoneApplicationPage.Resources>
  <local:AppSettings x:Key="PicoSettings"></local:AppSettings>
</phone:PhoneApplicationPage.Resources>

As soon as I add that the XAML parser throws a wobbler and the root PhoneApplicationPage gets the old blue squiggly and reports our favourite "element is already the child of another element" error. If I remove the resource clause, that error goes away and the xaml renders, but of course the textbox bindings then all throw an error because they cannot see their resources.

I've been googling this the last three hours and I can't see what's wrong, and none of the answers I've found here and elsewhere seem to fit. Can some kind soul show me the blindingly stupid thing I've done and please put me out of my misery?

Edit Here's the AppSettings class. It's just the Microsoft code sample, hacked into the code-behind:

namespace PicoSDU
{
public partial class AppSettings : PhoneApplicationPage
{
    // Our settings
    IsolatedStorageSettings settings;

    // The key names of our settings
    const string IpSettingKeyName    = "IpSetting";
    const string SysIdSettingKeyName = "SysIdSetting";
    const string WsIdSettingKeyName  = "WsIdSetting";
    const string PortSettingKeyName  = "PortSetting";

    // The default value of our settings
    const string IpSettingDefault    = "81.179.24.51";
    const string SysIdSettingDefault = "1";
    const string WsIdSettingDefault  = "511";
    const string PortSettingDefault  = "1846";

    public AppSettings()
    {
       InitializeComponent ();
       try
       {
          settings = IsolatedStorageSettings.ApplicationSettings;
       }
       catch (System.IO.IsolatedStorage.IsolatedStorageException e)
       {
          // handle exception
       }
    }

    public bool AddOrUpdateValue(string Key, Object value)
    {
        bool valueChanged = false;

        // If the key exists
        if (settings.Contains(Key))
        {
            // If the value has changed
            if (settings[Key] != value)
            {
                // Store the new value
                settings[Key] = value;
                valueChanged = true;
            }
        }
        // Otherwise create the key.
        else
        {
            settings.Add(Key, value);
            valueChanged = true;
        }
       return valueChanged;
    }

    public T GetValueOrDefault<T>(string Key, T defaultValue)
    {
        T value;

        // If the key exists, retrieve the value.
        if (settings.Contains(Key))
        {
            value = (T)settings[Key];
        }
        // Otherwise, use the default value.
        else
        {
            value = defaultValue;
        }
        return value;
    }

    public void Save()
    {
        settings.Save();
    }

    public string IpSetting
    {
        get
        {
            return GetValueOrDefault<string>(IpSettingKeyName, IpSettingDefault);
        }
        set
        {
            if (AddOrUpdateValue(IpSettingKeyName, value))
            {
                Save();
            }
        }
    }

    public string SysIdSetting
    {
       get
       {
          return GetValueOrDefault<string> ( SysIdSettingKeyName, SysIdSettingDefault );
       }
       set
       {
          if (AddOrUpdateValue ( SysIdSettingKeyName, value ))
          {
             Save ();
          }
       }
    }

    public string WsIdSetting
    {
       get
       {
          return GetValueOrDefault<string> ( WsIdSettingKeyName, WsIdSettingDefault );
       }
       set
       {
          if (AddOrUpdateValue ( WsIdSettingKeyName, value ))
          {
             Save ();
          }
       }
    }

    public string PortSetting
    {
        get
        {
           return GetValueOrDefault<string> ( PortSettingKeyName, PortSettingDefault );
        }
        set
        {
           if (AddOrUpdateValue ( PortSettingKeyName, value ))
            {
                Save();
            }
        }
    }
}
}

Upvotes: 1

Views: 742

Answers (1)

Igor Kulman
Igor Kulman

Reputation: 16361

Your code is quite bizzare. You are trying to embed one Page (your AppSettings class inherits from PhoneApplicationPage) into another. A much better approach would be to use the MVVM pattern.

Do not make the AppSettings inherit from PhoneApplicationPage and make it into a ViewModel. More info at http://msdn.microsoft.com/en-us/library/windowsphone/develop/gg521153(v=vs.105).aspx

Upvotes: 1

Related Questions