... a journey through WPF, MVVM and .NET4 RSS 2.0
# Wednesday, April 29, 2009

In the previous post, I wrote about using the MVVM pattern with Context Menus, but you can use it with top-level menus too -- for example, perhaps I'd like to manage a set of options through a set of checkboxes.  First, let's start with my list of options - for simplicity, we'll just make it an enumeration:

public enum Test
{
   A,
   B,
   C,
   D,
   E,
   F
}

Next, let's define a ViewModel to wrap this - each entry should support a single value and a boolean binding to determine whether it is checked:

public class CheckValues
{
   public Test Value { get; set; }
   public string Text { get { return Value.ToString(); } }
   public bool IsChecked { get; set; }
}

Very likely this object would also implement INotifyPropertyChanged and fire change notifications, in this case it's not necessary since we aren't changing the values from code behind.  The next step is to expose this as a bindable collection - since it's not changing we'll just use a List<T>.

public List<CheckValues> EnumValues { get; set; }

...

EnumValues = new List<CheckValues>();
foreach (object t in Enum.GetValues(typeof (Test)))
{
   EnumValues.Add(new CheckValues {Value = (Test) t});
}

Finally, in the XAML itself we want to databind to the collection of CheckValues, supplying the appropriate support for each MenuItem to bind the menu checks to the collection:

<Menu DockPanel.Dock="Top">
   <MenuItem Header="Enum Values" ItemsSource="{Binding EnumValues}">
      <MenuItem.ItemContainerStyle>
         <Style TargetType="MenuItem">
            <Setter Property="Header" Value="{Binding Text}" />
            <Setter Property="IsCheckable" Value="True" />
            <Setter Property="IsChecked" Value="{Binding IsChecked}" />
         </Style>
      </MenuItem.ItemContainerStyle>
   </MenuItem>
</Menu>

That gives us our nice "checkable" menu - when the user clicks a checkmark, it sets the appropriate boolean in the code behind which could trigger logic, set values, etc.

But wait!  Often with checkboxes you want mutual exclusive choices - like the RadioButton groups.  This was a feature of Windows Forms which unfortunately did not make it into WPF, but we can make it work that way.  We could use the above logic and have the selection of a specific CheckValue unselect any others, or we could take advantage of RadioButtons to do this -- unfortunately I've not found a way that doesn't invoice a little code behind so it's not quite as clean as I'd like it to be, but it is possible.

First, let's define an exclusive ViewModel object, here we will track the "current" selection:

public class ExclusiveCheckValues
{
   public static Test SelectedValue { get; set; }
   public Test Value { get; set; }

   public string Text { get { return Value.ToString(); } }
   public bool IsChecked
   {
      get { return Value == SelectedValue; }
      set
      {
         if (value)
            SelectedValue = Value;
      }
   }
}

Next, we populate it into a bindable collection just as before and then we create some slightly more complex XAML for our menu items.  The trick here is to put a RadioButton into the menu item and associate it with a group.  I have to give a shout out to Dr. WPF from the wpf-disciples list some kudos here - he posted this trick on the MSDN forums (http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/76c14163-9462-44d6-b0eb-6f22a006b423).  It turns out the MenuItem.Icon is positioned just where the normal check mark would be so if we place a styled RadioButton as the MenuItem.Icon we can make it look just like the checkmark:

<Menu DockPanel.Dock="Top">

   <Menu.Resources>
      <RadioButton x:Key="rb" x:Shared="false" HorizontalAlignment="Left"
                   GroupName="MenuGroup" IsChecked="{Binding IsChecked}">
         <RadioButton.Template>
            <ControlTemplate TargetType="{x:Type RadioButton}">
               <Path x:Name="check" Margin="7,0,0,0" Visibility="Collapsed" VerticalAlignment="Center"
                      Fill="{TemplateBinding MenuItem.Foreground}" FlowDirection="LeftToRight"
                      Data="M 0,5.1 L 1.7,5.2 L 3.4,7.1 L 8,0.4 L 9.2,0 L 3.3,10.8 Z"/>
               <ControlTemplate.Triggers>
                   <Trigger Property="IsChecked" Value="True">
                      <Setter TargetName="check" Property="Visibility" Value="Visible" />
                   </Trigger>
                </ControlTemplate.Triggers>
             </ControlTemplate>
          </RadioButton.Template>
       </RadioButton>
    </Menu.Resources>

    <MenuItem Header="Single Select Enum Values" ItemsSource="{Binding ExclusiveEnumValues}">
       <MenuItem.ItemContainerStyle>
          <Style TargetType="MenuItem">
             <Setter Property="Header" Value="{Binding Text}" />
             <Setter Property="Icon" Value="{DynamicResource rb}" />
             <EventSetter Event="Click" Handler="OnMenuItemClick" />
          </Style>
       </MenuItem.ItemContainerStyle>
    </MenuItem>
</Menu>

Here, we've placed the RadioButton into our resources - but marked it as non-shareable.  The issue is that Styles always share property setters - and if you tried to do this:

<Style TargetType="MenuItem">
   <Setter Property="Icon"
>
     <Setter.Value>
       <RadioButton ... />
     </Setter.Value>
   </Setter>

You will get a very strange error message: "Cannot assigned type 'RadioButton' to type 'object'".  Which makes absolutely no sense until you realize the XAML parser is trying to assign the same RadioButton to every MenuItem.Icon.  The non-shareable resource fixes that issue.  The last step is to set the check -- this is done through the EventSetter in the above template.  It's wired to the little bit of code behind:

private void OnMenuItemClick(object sender, RoutedEventArgs e)
{
   ((RadioButton) ((MenuItem) sender).Icon).IsChecked = true;
}

This then sets the IsChecked property which will, in turn, select that item.  Now you can look at ExclusiveCheckValue.SelectedValue to get the current item.  This isn't the most elegant solution, nor is it the only solution but it's an interesting one to think on.  If I were truly building this into a production application, I'd probably look at controlling the selection in the ModelView (i.e. enumerate through my other choices and deliberately turn them off).  But this does illustrate how powerful WPF is!

Here's the example project with both styles.


 

Wednesday, April 29, 2009 8:30:51 AM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
.NET | WPF
# Tuesday, April 21, 2009

One question I've fielded a couple of times is how to manage menus, primarily context menus, with the MVVM pattern.  It turns out to be pretty easy once you know the "trick".  The key thing to keep in mind is that menus are just ItemsControls - they support data templating and binding like any other ItemsControl.  However, the part where people get lost is in hooking up the commands.  Here's the trick:

Step 1: Define some code behind construct to manage each menu item in a hiearchial fashion.  Here's one I've used:

public class MenuItem
{
   public string Text { get; set; }
   public List<MenuItem> Children { get; private set; }
   public ICommand Command { get; set; }
  
   public MenuItem(string item)
   {
       Text = item;
       Children = new List<MenuItem>();
   }
}

Step 2: Create your menu structure using the above container.

This involves just creating a List<MenuItem> which holds the root nodes and using a DelegatingCommand to wire up some code behind method.  Here's an example:

public List<MenuItem> MenuOptions
{
    get  {

       var menu = new List<MenuItem>();
       if (SupportedFileFormats.Count > 0)
      {
          var mi = new MenuItem("O_pen");
          foreach(var fl in SupportedFileFormats)
          {
               var sff = fl;
               mi.Children.Add(new MenuItem(fl.Attributes.Description) 
                      { Command = new DelegatingCommand(() => { LoadFromFormat(sff); })});
          }
          menu.Add(mi);
       }

       menu.Add(new MenuItem("Close _All") { Command = new DelegatingCommand(OnCloseAll, () => FileList.Count > 0)});
       return menu;
    }

}

Note the use of an anonymous method to suck the correct file format into the command handler.  A nice trick so the command is executed with some contextual information of what you clicked on.

Finally, you need to setup the context menu style:

<Style x:Key="ContextMenuItemStyle">
  
<Setter Property="MenuItem.Header" Value="{Binding Text
}"/>
   
<Setter Property="MenuItem.ItemsSource" Value="{Binding Children
}"/>
   
<Setter Property="MenuItem.Command" Value="{Binding Command
}" />
</Style>

And then use the style where you want the menu to appear:

<StackPanel Orientation="Horizontal">
   
<Image Source="{Binding Image}" Width="16" Height="16" />
   
<TextBlock Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center" Text="{Binding Header}" />
   
<StackPanel.ContextMenu>
      
<ContextMenu ItemContainerStyle="{StaticResource ContextMenuItemStyle}" ItemsSource="{Binding MenuOptions}" />
   
</StackPanel.ContextMenu>
</StackPanel>

</HierarchicalDataTemplate>

Now the context menu is populated and properly dispatches to your ViewModel commands!

Edit: added intermediate object in LINQ query to fix deferred query issue pointed out in comments.
 

Tuesday, April 21, 2009 5:35:59 PM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
.NET | WPF

I recently wrote about the mini-library I tend to use, but Bill Kempf (one of the WPF disciples) released a MVVM library for WPF called Onyx which looks very appealing. I especially like his use of static reflection to implement INotifyPropertyChanged. Sweet Bill! Thanks for sharing!


Check it out: WPF Onyx

Tuesday, April 21, 2009 2:29:47 PM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
WPF
# Friday, April 17, 2009

There's been a lot of talk about the Model-View-ViewModel pattern recently and it's usage around the WPF and Silverlight technology stack.  When teaching WPF, I always introduce students to MVVM as part of the Essential WPF class, it's an incredibly useful pattern that really separates the UI from the code behind behavior.  One of the things I give the students is a library to do MVVM - I also use it in my consulting work.  With all the focus on it lately, I figured maybe it's time to release it to the public.

A bit of history -- the library is really just a place where I dump all kinds of useful utility classes, helpers, wrappers, etc. that I tend to use a lot.  I started it about 3 years ago and it wasn't originally intended to be just an MVVM implementation so you'll find it's got all kinds of stuff in it, not all of which is MVVM specific.  It's evolution owes a lot to various blog posts, WPF Disciples, and other WPF leaders; I certainly didn't invent anything radically new but borrowed heavily from all kinds of places as I built various classes I needed for my own work.  These classes tended to evolve with new functionality (either due to necessity, or because a good idea occurred to me or someone else). For example, there was a recent thread on the Mediator pattern (initiated by Marlon Grech and added on by Josh Smith, Laurent Bugnion and others); I already had a message mediator in place but the idea of using an attribute to hook it up was a great one that I adopted into my library just because I like the idea.  The Delegating command pattern is one you see in a lot of places - including the Prism implementation.  The event routing attached behavior was made possible by a couple of blog posts by Mike Hilberg and John Gossman. So, be aware that as you use this code, it owes a lot to a variety of people.  That said, any bugs or issues are completely mine and I take full credit for them.

So, what all is here?  Well, quite a bit.  As I said, this is a collection of helpers I've built and reused over the years doing WPF consulting and instruction.  When MVVM came to my notice I worked on trying to completely separate the XAML side so I'll focus on those classes here. 

The basic idea is to derive your ViewModel classes from one of three base classes depending on what you need:

JulMar.Windows.Mvvm.ViewModel - supports basic INotifyPropertyChanged and Close/Activate eventing to the view.
JulMar.Windows.Mvvm.ValidatingViewModel - supports everything above, adds Validation through IDataErrorInfo support
JulMar.Windows.Mvvm.EditingViewModel - supports basic + validation + IEditableObject

Next, in each view (XAML) you set the DataContext property to your view model, the library has a MarkupExtension to do the work for you -

<Window x:Class="TestMvvm.MainWindow"
  
xmlns:julmar="http://www.julmar.com/wpfhelpers"  xmlns:me="clr-namespace:TestMvvm"
   DataContext="{julmar:ViewModelCreator ViewModelType={x:Type me:WinViewModel}}">

This creates the view and also wires up support for closing the view and activating the view through the ViewModel class (using the RaiseCloseRequest and RaiseActivateRequest methods).

Everything is driven off ICommand - you can bind commands to the lifetime of the view (so you can detect activation, deactivation, loading, closing) through the LifetimeEvents attached behavior:

<Window x:Class="TestMvvm.MainWindow"
   julmar:LifetimeEvent.Activated="{Binding ActivatedCommand}"
  
julmar:LifetimeEvent.Close="{Binding CloseCommand}"
  
julmar:LifetimeEvent.Loaded="{Binding LoadedCommand}"
  
julmar:LifetimeEvent.Deactivated="{Binding DeactivatedCommand}" >

These execute the bound command in the ViewModel when those events occur in the view.  If you need other events styles you can use the EventCommander attached behavior which allows any arbitrary event to be wired to a command.  This can be placed on any UIElement:

<julmar:EventCommander.Mappings>
  
<julmar:CommandEvent Command="{Binding MouseEnterCommand}" Event="MouseEnter" />
  
<julmar:CommandEvent Command="{Binding MouseLeaveCommand}" Event="MouseLeave" />
julmar:EventCommander.Mappings>

You can also wire up keyboard and mouse gestures to commands using the InputBinder attached property:

<julmar:InputBinder.Bindings>
   <julmar:KeyBinding Command="{Binding ExitCommand}" Key="F3" Modifiers="ALT" />
  
<julmar:MouseBinding Command="{Binding ExitCommand}" Gesture="Control+RightClick" />
julmar:InputBinder.Bindings>

This replaces the traditional InputBindings collection with a data bindable version - it also supports CommandParameter objects on each KeyBinding or MouseBinding.  You can use it on any element which supports input bindings.

There are some helper classes implemented through interfaces and a (very) simple ServiceProvider to locate them:

IErrorVisualizer - to display errors (title + message).  Default implementation displays MessageBox with OK button.
IMessageVisualer - to display messages with a prompt.  Default implementation uses MessgeBox.
INotificationVisualizer - to display a wait prompt.  Default implemation uses an hourglass cursor.
IUIVisualizer - to display other views through a key.  Supports both modal and modaless display.

You use the ServiceProvider to find the services - the base ViewModel class has support built in for it:

Resolve<IErrorVisualizer>().Show("An Error Occurred", e.Description);

You can register the default implementation for all of the above in the App.xaml.cs file -

ViewModel.RegisterKnownServiceTypes();

You can also provide your own implementation through the ServiceProvider itself.  The ViewModel class has a public static field:

ServiceProvider.Add(typeof(IErrorVisualizer), new MyErrorVisualizer());

This replaces or adds the given service (using the type as the key) to the registry database.  You then use Resolve to find the service at runtime in any view.  Creating secondary views is done through the IUIVisualizer interface.  The default implementation provides a registry and must be added explicitly to use it:

IUIVisualizer controller = new UIVisualizer(new Dictionary
                                        {
                                            {Dialogs.AddNewPage, typeof(AddNewPageWindow)},
                                            {Dialogs.ManagePages, typeof(BrowseWindow)},
                                            {Dialogs.NewLogon, typeof(LoginDialog)}
                                        });

ServiceProvider.Add(typeof(IUIVisualizer), controller);

This adds three UI dialogs to the visualizer - the key is a simple string, the second parameter is a Type object that represents the Window to create.  You then get the window to display through the IUIVisualizer interface:

LoginViewData ld = new LoginViewData();

IUIVisualizer uiController = ServiceProvider.Resolve();
if (uiController.ShowDialog(Dialogs.NewLogon, ld).Value == true)
{
}

This shows the login dialog, using the LoginViewData as the DataContext (ViewModel).  It displays it as a modal dialog and returns the final result.  You can also display modaless dialogs which take an optional completion proc:

public bool Show(string key, object state, bool setOwner, EventHandler completedProc)

The completed procedure is invoked when/if the window ever closes.  Again, the state passed is the data context and if it derives from ViewModel the appropriate Closing/Activated events will be wired up to the events in the ViewModel.

I've been using this library for quite a while and it works great (for me).  I provide it here with full source code so you can diagnose any issues you have, or just look through it.  Feel free to use it however you like.  I don't claim this to be the end-all implementation - as I said much of the ideas expressed in here can be found elsewhere.  There is also a help file provided and a simple example of how to use some of the classes provided that I whipped up for fun.

Here's the download with everything packaged.

Enjoy - let me know if you have any issues or think of a good addition!  Just remember I put no guarentee on this code - consider it a sample for you to do whatever you like with.

 

Friday, April 17, 2009 11:06:09 AM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
.NET | WPF
# Wednesday, April 01, 2009

One annoying thing about ObservableCollection<T> is that it doesn't support modifications from background threads.  That is to say, the CollectionChanged notification doesn't marshal to the proper thread when it is raised and it causes the exception "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread". 

I ran into this problem a while back and searched out to see if someone else had solved it. I found a solution from Tamir Khason (a fellow WPF disciple), he wrote a thread-safe version (http://blogs.microsoft.co.il/blogs/tamir/archive/2007/04/22/Thread-safe-observable-collection.aspx), but I didn't want to add the locking into the collection itself (I want to manage it at a higher level myself).  Really, all I want is to do the notification on the proper (Dispatcher) thread. 

It turns out to be pretty easy, here's my solution:

public class MTObservableCollection<T> : ObservableCollection<T>
{
   public override event NotifyCollectionChangedEventHandler CollectionChanged;
   protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   {
      var eh = CollectionChanged;
      if (eh != null)
      {
         Dispatcher dispatcher = (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()
                 let dpo = nh.Target as DispatcherObject
                 where dpo != null
                 select dpo.Dispatcher).FirstOrDefault();

        if (dispatcher != null && dispatcher.CheckAccess() == false)
        {
           dispatcher.Invoke(DispatcherPriority.DataBind, (Action)(() => OnCollectionChanged(e)));
        }
        else
        {
           foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList())
              nh.Invoke(this, e);
        }
     }
  }
}

Wednesday, April 01, 2009 12:17:37 PM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
.NET | WPF
# Thursday, March 05, 2009

A couple of weeks ago, Jason Whittington (a fellow instructor at Developmentor) and I were doing a talk on asynchronous programming and we started with a very simple example of the APM -- using two loops to create and then consume the IAsyncResult work:

static void TwoLoopMain(string[] args)
{
   Queue<IAsyncResult> ars = new Queue<IAsyncResult>();
   Func<int,int,int> mathProc = Multiply;

   for (int i = 1; i <= 20; i++)
   {
      for (int j = 1; j <= 20; j++)
         ars.Enqueue(mathProc.BeginInvoke(i, j, null, null));
   }
   for (int i = 1; i <= 20; i++)
   {
      for (int j = 1; j <= 20; j++)
      {
         int result = mathProc.EndInvoke(ars.Dequeue());
         Console.SetCursorPosition(i * 3, j);
         Console.Write(result);
     }
  }
}

We had already presented a talk on LINQ earlier in the week and so I thought we might be able to do the above in a single expression with LINQ - kind of a challenge .. here's what we came up with:

static void LinqTest()
{
   Func<int,int,int> mathProc = Multiply;

   (from i in Enumerable.Range(1, 20)
    from j in Enumerable.Range(1, 20)
    select new { i, j, ar = mathProc.BeginInvoke(i, j, null, null) })
      .ToList()
      .ForEach(e => {
        int result = mathProc.EndInvoke(e.ar);
        Console.SetCursorPosition(e.i * 3, e.j);
        Console.Write(result);
     });
}

It's not easily readable (and so I wouldn't promote this for production code), but it's way cool and an example of how LINQ (and functional programming in general) is really changing the way programmers think about code.. just freaking cool..

Thursday, March 05, 2009 11:27:19 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
.NET
# Monday, February 23, 2009

I use a lot of virtual machines - and it's always handy to store files onto the real machine through shared folders. With Windows 7 and the new library feature I thought it would be really cool to add the shares to the Documents Library but found that the underlying system must be running Indexing Server 4.0 -- not an option for my OSX based system!

Here's a nice little workaround that lets you add non-indexed folders though:

To add a non-indexed UNC as a library to Windows 7 Beta:

1. Create a folder on your hard drive for shares. I used c:\users\Mark\Shares\Documents.
2. Add the new folder to your library using Explorer.
3. Delete the folder using a command prompt window.
4. Use the mklink command to create a symbolic link with the Documents name to your share. In my case the proper command was:

mklink /D Documents \\.Host\Documents

Note that mklink.exe requires administrator access so start the command prompt by holding SHIFT+CTRL and clicking on it (or use the "Run As Administrator" option)


Voila! Explorer keeps the folder in the library even though it's not indexed.
Monday, February 23, 2009 8:33:36 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -

# Thursday, January 29, 2009
Update
Microsoft has released the API code pack including Windows 7 support -- get it here: http://code.msdn.microsoft.com/WindowsAPICodePack

Well, it's been a while since I posted anything, I'm sorry!

I've been busy working with Windows 7 and Microsoft Surface touch-computing. To that end, I needed to get access to some of the new Windows 7 APIs in managed code ... which isn't supported yet (but is coming).

So I built an interop library to access:

  • Scenic Ribbon (native Win32 ribbon) in Windows Forms
  •  Native WM_TOUCH and WM_GESTURE messages
  •  Sensor API
  •  Shell APIs (jump lists, thumbnail buttons, libraries)
One note: I make no guarantee that everything is correct (it's hard to do that on a shifting beta with minimal docs, but in addition I'm not sure I've gotten 100% coverage with everything anyway). Here's the interop library with source code:

Windows 7 Beta 1 Interop Library for .NET 2.0

I'll be posting more details and samples a bit later, here's a couple to get you started.

Here's a simple example of using the Scenic Ribbon and native touch support to create a (very) simple Windows Forms finger
 paint program:

touch_img.jpg

  Multi Touch example with Windows Forms

Here's a simple example of using the gestures and library support in a WPF application. It grabs all the directories in your Pictures Library and then shows you each picture and lets you use the swipe gesture to move between then, pinch to scale and of course, rotate.

gesture_img.jpg

Gesture example with WPF

Both of these samples work with the HP Touchmate (and multi-touch drivers) and Windows 7 Beta 1. Have fun! -mark
Thursday, January 29, 2009 12:02:21 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
.NET | Code | WPF
# Thursday, November 13, 2008

I've been playing with the Silverlight toolkit (released at PDC) this week and ran across a pretty nasty edge case related to HeaderedContentControl and the implicit style manager.  If you create something like this, where the Header is set to a Visual of some kind:

<UserControl x:Class="SilverlightPrototype.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls">

<StackPanel x:Name="LayoutRoot" Background="White">
   <Button Click="Button_Click" Content="Change Style" />
   <Controls:HeaderedContentControl>
      <Controls:HeaderedContentControl.Header>
         <TextBlock Text="Header" Foreground="DarkBlue" FontWeight="Bold" />
      </Controls:HeaderedContentControl.Header>
      <TextBlock Text="Body" />
   </Controls:HeaderedContentControl>
</StackPanel>

</UserControl>

And then you change the style of the headered control when the button is clicked:

private void Button_Click(object sender, RoutedEventArgs e)
{            
    Uri uri = new Uri(@"SilverlightPrototype;component/theming/simplestyle.xaml", UriKind.Relative);
    ImplicitStyleManager.SetResourceDictionaryUri(LayoutRoot, uri);
    ImplicitStyleManager.SetApplyMode(LayoutRoot, ImplicitStylesApplyMode.OneTime);
    ImplicitStyleManager.Apply(LayoutRoot);
}

Where the style is just a simple ContentPresenter or ContentControl for the header and the body:

<Style TargetType="Controls:HeaderedContentControl">
   <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="Controls:HeaderedContentControl">
           <StackPanel Orientation="Horizontal">
              <ContentPresenter Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" Margin="10" />
              <ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" Margin="10" />
           </StackPanel>
        </ControlTemplate>
     </Setter.Value>
   </Setter>
</Style>

Silverlight will crash deep in the DependencyProperty.SetValue code with an ArgumentException indicating that the TextBlock for the header is invalid.  The issue appears to be that the TextBlock defined as the header content is already part of the visual tree and therefore cannot be bound to the new control template.  Content works fine so there is some other path being taken for that property.

The workaround is pretty easy, just use a non-visual in the header and then supply a DataTemplate to give it the appropriate visual tree.  Silverlight will re-create the visual tree each time from the data template so we don't have the reuse issue:

<StackPanel x:Name="LayoutRoot" Background="White">

<StackPanel.Resources>
   <!-- Header works as long as template is supplied - i.e. non-visual element applied directly to header -->
   <DataTemplate x:Key="HeaderTemplate">
      <TextBlock Foreground="DarkBlue" FontWeight="Bold" Text="{Binding}" />
   </DataTemplate>
</StackPanel.Resources>

<Button Click="Button_Click" Content="Change Style" />

<Controls:HeaderedContentControl HeaderTemplate="{StaticResource HeaderTemplate}">
   <Controls:HeaderedContentControl.Header>
      <sys:String>Header Text</sys:String>
   </Controls:HeaderedContentControl.Header>

   <TextBlock Text="Body" />
</Controls:HeaderedContentControl>

</StackPanel>

If you need more than a string, then just define an object and assign that instead - for example:

class HeaderStuff
{
   public string Text { get; set; }
   public bool IsChecked { get; set; }
}

<DataTemplate x:Key="HeaderStuff">
   <StackPanel>
      <Rectangle Fill="Blue" Stroke="Yellow" Width="50" ... />
      <CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Text}" />
   </StackPanel>
</DataTemplate>

...

<Controls:HeaderedContentControl HeaderTemplate="{StaticResource HeaderStuff}">
   <Controls:HeaderedContentControl.Header>
      <me:HeaderStuff IsChecked="True" Text="This is a checkbox" />
   </Controls:HeaderedContentControl.Header>
</Controls:HeaderedContentControl.Header>


Thursday, November 13, 2008 9:42:01 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
.NET
# Wednesday, November 12, 2008
One of the coolest new debugging features included with VS2010 CTP1 is the management of WPF event traces directly in the IDE.  With this feature you can turn specific event traces on and off and have them show up in the debug window.  The default setting is to just show data binding errors - the existing behavior.  But now you can view resource lookups, routed event creation, etc. Here's the dialog:


Wednesday, November 12, 2008 1:06:04 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
.NET | WPF
I'm a WPF Disciple
Search
Categories
Archive
<April 2009>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Mark Smith
Sign In
All Content © 2010, Mark Smith
DasBlog theme 'Business' created by Christoph De Baene (delarou)