2

I have a basic WinRT XAML UserControl with a single dependency property as implemented below. The user control is apparently only constructed design-time when used in another Page or UserControl. The text "Hello world" is not rendered when I work in the designer with the user control itself. How can I make the designer initialize the user control with data also in this case?

XAML:

<UserControl
    x:Class="GTWin8.Ui.SimpleBinding"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:GTWin8.Ui"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400"
    x:Name="ThisControl">

    <Grid Background="Black">
        <TextBlock Text="{Binding Path=Message, ElementName=ThisControl}" 
                   HorizontalAlignment="Left" Margin="10,10,0,0" 
                   TextWrapping="Wrap" VerticalAlignment="Top" Height="280" Width="380"/>
    </Grid>
</UserControl>

Code-behind:

public sealed partial class SimpleBinding : UserControl
{
    public static DependencyProperty MessageProperty = DependencyProperty.Register(
           "Message", typeof(String), typeof(SimpleBinding), new PropertyMetadata(null));

    public String Message
    {
        get { return (String)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    public SimpleBinding()
    {
        this.InitializeComponent();
        Message = "Hello world";
    }
}
Jerry Nixon
  • 31,313
  • 14
  • 117
  • 233
Nilzor
  • 18,082
  • 22
  • 100
  • 167
  • I found [this thread](http://stackoverflow.com/questions/1889966/what-approaches-are-available-to-dummy-design-time-data-in-wpf) which describes how to acheieve design-time data. By moving the Message property into a separate ViewModel class and using the design time DataContext, I can acheieve what I want. – Nilzor Aug 09 '12 at 11:10

2 Answers2

5

Well, a simpler method is declarative invocation.

<UserControl>

    <d:Page.DataContext>
        <sample:FakeViewModel />
    </d:Page.DataContext>

    <Grid>
        <!-- your bind/content -->
    </Grid>

</UserControl>

I use this in all my projects because it delivers design time data without interfering with my runtime experience. More: http://blog.jerrynixon.com/2012/06/windows-8-15-more-reasons-why-i-choose.html

Jerry Nixon
  • 31,313
  • 14
  • 117
  • 233
  • 1
    This is basically the method I ended up using, taken from the SO question linked to in my comment to the original question. I'll leave this as the accepted answer. – Nilzor Aug 09 '12 at 18:36
  • Yeah, that's a great approach. So easy. And solves the current problem of Blend not yet supporting data generation in 2012. – Jerry Nixon Aug 09 '12 at 20:33
3

IIRC, in the direct form you wrote, this is impossible to achieve, because in the Design Time you do not work with an instance of your control. Pay attention to the first lines of the XAML: the root element is specifying a UserControl, the base class. This is the instance you work with! This is why there were always been problems with designing components with abstract base classes. Everything you play in the designer with is a UserControl instance, filled with the content specified by XAML.

The XAML parser instantiates any objects that are described by tag names. As your are designing your control, the parser is prevented from instantiating it, because the control actually does not exist yet. This is exactly why the first word of the XAML is UserControl and the your class name is given as x:Class attribute. When you save the XAML, it will get preprocessed to MyControl.g.cs file that specified a partial-class, it will get compiled along with pair with your own MyControl.cs, and only then the Visual Studio (or your app) will be able to invoke code that it contains.

If you try hard, you may force the XAML parser to load your control, but what for? You will have a snapshot of PREVIOUS build, not the currently-edited one. Only XAML templates are kept up-to-date, because they are dynamically dropped and loaded.

That's for the part "why did my constructor did not run".

And if you need the Message to get set in ctor, why don't just set it as the default property value?

public sealed partial class SimpleBinding : UserControl
{
    public static DependencyProperty MessageProperty = DependencyProperty.Register(
           "Message", typeof(String), typeof(SimpleBinding), new PropertyMetadata("Hello world"));
...
    public SimpleBinding()
    {
        this.InitializeComponent();
    }
}

Of course, it is heavily limited and hardcoded, in constrast to what you could branch/calculate/etc in the constructor. If you need more freedom, try playing with Design Time Data, maybe that will help a little.

If you really need to run custom code inside your component at its own individual design time, consider moving the code into a dummy base class and put <MyBaseClass> instead of <UserControl> at the root of the XAML. The XAML parser will be forced to instantiate it and then skin it with the contents of xaml. Just be aware that you will invoke the previous successful build of that base class. Also, remember to use IsInDesignMode or similar (see ie. https://stackoverflow.com/a/426072/717732) for filtering the behaviour.

Community
  • 1
  • 1
quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • Your answer is correct, but I fear I asked the wrong question. If I move the databound properties to a ViewModel, it is indeed possible to get design-time only data visible (although the control itself still is not constructed). I will describe this method in a comment to the original post. – Nilzor Aug 09 '12 at 11:08