views:

1860

answers:

2

Hi, I am designing a WPF user control which contains other user controls (imagine a WidgetContainer, containing different Widgets) - using M-V-VM architecture. During development, I have WidgetContainerView in a window, window (View) spawns a WidgetContainerViewModel as its resource, and in a parameterless constructor of WidgetContainerViewModel, I fill its exposed collection with some sample widgets (WidgetViewModels).

WidgetContainer control inherits the DataContext from window, and inside, there is a ListView, that binds Widgets to WidgetView control (which is inside ListView.ItemTemplate).

Now this works OK in my WindowView, as I see my sample widgets, but once I edit the WidgetContainerView or WidgetView, there is no content - at design time, controls are standalone, and they don't inherit any DataContext, so I don't see a content, and have troubles designing them (a ListView is empty, Widget's fields as well...).

I tried adding a sample widget to the WidgetView:

public partial class WidgetView : UserControl
{
 public WidgetView()
 {
  InitializeComponent();
  if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
  {
   //btw, MessageBox.Show(...) here sometimes crashes my Visual Studio (2008), but I have seen the message - this code gets executed at design time, but with some lag - I saw the message on reload of designer, but at that time, I have already commented it - wtf?
   this.DataContext = new WidgetViewModel(); //creates sample widget
  }
 }
}

but that didn't work - I still don't see anything in designer.

I also wanted to create a WidgetViewModel as a resource in WidgetView, like this:

<UserControl x:Class="MVVMTestWidgetsControl.View.WidgetView"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 DataContext="WidgetViewModel" //this doesn't work!
 Height="Auto" Width="Auto">
 <UserControl.Resources>
  <ResourceDictionary>
   <ViewModel:WidgetViewModel x:Key="WidgetViewModel" />
  </ResourceDictionary>
 </UserControl.Resources>

 <TextBlock Text="{Binding Path=Title}"></TextBlock>

</UserControl>

but I don't know how to assign a WidgetViewModel as a DataContext of a whole widget - I can't add DataContext attribute to UserControl, because WidgetViewModel is defined later in the code. Any ideas how to do this? I could use a sample data this way, and just override it in code so that it has the right content at runtime...

What are your best practices when developing user controls? Thank you, designing empty control is no fun :)).

+3  A: 

In your second snippet, you should be able to refer to your DataContext as a DynamicResource:

DataContext="{DynamicResource WidgetViewModel}"

But most custom user controls have some sort of top level layout container, and you can set the DataContext on that container as a StaticResource.

In your case, however, you may want to consider dropping the VM portion of your code altogether since you're writing a custom UserControl. You should ask yourself what benefits are you gaining from a completely self-contained ViewModel with no real backing Model designed for just one View (i.e. the custom UserControl). Perhaps you could just define some DependencyProperties and use those?

micahtan
Thank you, this worked well (although I'd rather prefer a solution that wouldn't change program at all at runtime).
Tomáš Kafka
A: 

I came up with several solutions: Add DC as resource (it will get automatically instantiated with parameterless constructor), and do the following in View's codebehind:

 public PanelView()
 {
  InitializeComponent();

  if (!DesignerProperties.GetIsInDesignMode(new DependencyObject())) //DeleteAtRelease:
  {
   //we are in runtime, reset DC to have it inherited
   this.DataContextHolder.DataContext = DependencyProperty.UnsetValue;
  }

 }

Better way would be to only assign DC if we are at designtime, but VS didn't like it - it worked only sometimes, and quite nondeterministically, and once it even crashed.

Other check for design time is:

  if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
  {
   this.DataContext = new WidgetViewModel();
  }
Tomáš Kafka