Assigning ViewModels to UserControls at runtime and at design time - wpf

I am writing some data visualization code in WPF with MVVM Light. Here's a fragment:
<Window x:Class="EventBlockVisualization.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ignore="http://www.ignore.com"
Title="MainWindow"
mc:Ignorable="d ignore"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ItemsPanelTemplate x:Key="GraphRowItemsPanelTemplate">
<StackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</Window.Resources>
<Grid IsSharedSizeScope="True">
<ScrollViewer Margin="8" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True">
<ItemsControl x:Name="GraphItemsControl" Margin="8" ItemsSource="{Binding VibeEvents, Mode=OneTime}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="NameWidthSizeGroup" Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="NameTextBlock" Text="{Binding Name}" Grid.Column="0" Margin="4,0"/>
<ItemsControl x:Name="GraphRowItemsControl" ItemsSource="{Binding VibeEventViewModels, Mode=OneTime}" ItemsPanel="{DynamicResource GraphRowItemsPanelTemplate}" Grid.Column="1" Margin="4,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" VerticalAlignment="Center" Height="10">
<TextBlock x:Name="FGTitleTextBox" Text="{Binding FGTitle}" Visibility="Collapsed"/>
<Button Margin="1,0,0,0" Width="{Binding LengthInSeconds}" HorizontalAlignment="Left" Background="{Binding BackgroundColor}" BorderBrush="#FF2186A1">
<Button.ToolTip>
<ToolTip>
<StackPanel>
<TextBlock FontWeight="Bold" Text="{Binding FGTitle}"/>
<TextBlock Text="{Binding LengthText}"/>
</StackPanel>
</ToolTip>
</Button.ToolTip>
</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Window>
I'd like to swap out the central ItemsControl.ItemTemplate DataTemplate and make it a User Control so that I can design it more easily in Expression Blend.
I cannot find a simple sample that includes a User Control in MVVM Light but there are some tutorial articles. For example in MVVM Instantiation Approaches (Option 6) Paul Stovell suggests binding in the UserControl's ViewModel in MVVM Light thus:
<UserControl ...>
<UserControl.Resources>
<ViewModelLocator x:Key="ViewModelLocator"/>
</UserControl.Resources>
<TextBox Text="{Binding Source={DynamicResource ViewModelLocator}, Path=CalculatorViewModel...}" />
That would work great when I'm designing the UserControl in Expression Blend as the locator can supply a ViewModel replete with dummy data. But what happens at runtime; how does that binding get overwritten with instances of the UserControl's ViewModel class supplied by collections in the main ViewModel? The same problem happens for the MainWindow at design time. If I am working in Expression Blend on the look and feel of the MainWindow how does that binding get overwritten with instances of the UserControl's ViewModel class supplied by collections in the design time main ViewModel?
There are a number of questions and answers that already touch on this:
In https://stackoverflow.com/a/3334780/575530 akjoshi suggests that the main ViewModel holds the instance of the UserControl's ViewModel; but how does that work when I am designing the UserControl itself?
In https://stackoverflow.com/a/9910298/575530 tam points out that "you want to keep your datacontext open and available for binding to in controls where you are using this control" and in the following comment SoMoS adds that one needs to "create properties in the ViewModel for the binded properties and when someone wants to change one property of the control (like some subcontrol enabled) he will have to go thru the View Model". That's promissing but I am not sure what to do in place of the MainViewModel's bindable collection of UserControlViewModels.
In https://stackoverflow.com/a/6340668/575530 Ehsan Ershadi suggests that "it's not a good idea to use MVVM Light ViewModelLocator for UserControles because it is a static property and when you are going to instantiate multiple instances of your user control there are going to have the same common ViewModel so they all act same and this is not what we want for a UserControl in case you decide to use it once in your entire project." And then states that "to get around this problem you need to modify the ViewModelLocator by making all the properties Non static for instance". I'm not sure how that would help me.
In the comments after https://stackoverflow.com/a/2637830/575530 Jon Mitchell mentions that "It does look like MVVM isn't ideal for creating user controls". I hope that's not right.
In contrast, in When should I use a UserControl instead of a Page? dthrasher mentions that "many of the WPF MVVM frameworks seem to avoid using the NavigationWindow and Page controls in favor of composing pages using nested UserControls", i.e. that UserControls are commonplace devices in MVVM.
In https://stackoverflow.com/a/1798649/575530 Reed Copsey reminds sandbox that "UserControls can always talk to their containing control via exposing properties and using DataBinding. This is very nice, since it preserves the MVVM style in all aspects." and that "The containing control can use properties to link two properties on two user controls together, again, preserving clean boundaries" But again I don't see how this helps when I am in Expression Blend designing the UserControl.
In Should I be using UserControls for my Views instead of DataTemplates? Rachel mentions occasionally using Expression Blend to design the UserControl before cutting and pasting the code into a DataTemplate: "in the event I do want to use it to design a DataTemplate, I usually create a new UserControl, design it the way I want it, then copy/paste the contents into a DataTemplate"
Sorry about this essay length question! I am confused about how to use MVVM Light when designing a UserControl destined to be the visual for the items in a collection on the MainWindow, especially how to set up the three bindings: run-time view models, design time view models for the main window and its instantiations of the user control, and a design time view model for the user control in isolation.

I think you're overcomplicating things:
What's wrong with:
<Grid IsSharedSizeScope="True">
<ScrollViewer Margin="8" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True">
<ItemsControl x:Name="GraphItemsControl" Margin="8" ItemsSource="{Binding VibeEvents, Mode=OneTime}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<MyShinyUserControl DataContext={Binding}/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
Bind each VibeEvent to the DataContext of the user control. In the user control itself I'd suggest creating a design-time DataContext to make design easier. Design-Time DataContext looks like this:
<UserControl x:Class="EMC.Windows.AlarmsModule.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:AlarmsModule="clr-namespace:EMC.Windows.AlarmsModule" d:DesignHeight="300"
d:DesignWidth="300"
d:DataContext="{d:DesignInstance Type=AlarmsModule:Alarm}"
>
This gets you to a place where you can build your user control and have design-time data in it. And it's simple and doesn't require much, if any, scaffolding.

Based on Faster Solutions' answer here's the simplest example I can come up with of using a UserControl to display the contents of a list within MVVM Light.
For completeness sake I'll include all the code which I have tried to make as short as possible while still providing design time data that differs from run time data in both the user control's view model and the main view model.
Firstly the locator, VMUCExample/ViewModel/ViewModelLocator.cs:
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
namespace VMUCExample.ViewModel
{
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<ASquareViewModel>();
}
public ASquareViewModel ASquare
{
get
{
return ServiceLocator.Current.GetInstance<ASquareViewModel>();
}
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public static void Cleanup() {}
}
}
I've not used the data service side of MVVM Light, partly to keep things simple. The visible distinction between live data and design time data is handled in the two view model classes.
VMUCExample/ViewModel/ASquareViewModel.cs:
using System.Windows.Media;
using GalaSoft.MvvmLight;
namespace VMUCExample.ViewModel
{
public class ASquareViewModel : ViewModelBase
{
private Brush _SquareColour;
public Brush SquareColour
{
get
{
return _SquareColour ?? (_SquareColour = IsInDesignModeStatic ?
new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0x78, 0x78)) : // FF7878 (pastel red)
new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xBB, 0x78))); // FFBB78 (nectarine)
}
set { _SquareColour = value; }
}
}
}
So looking at the user control in Expression Blend I see a simple rectangle with pastel red fill:
The main view model lives in the file VMUCExample/ViewModel/MainViewModel.cs:
using System.Collections.Generic;
using System.Windows.Media;
using GalaSoft.MvvmLight;
namespace VMUCExample.ViewModel
{
public class MainViewModel : ViewModelBase
{
private List<ASquareViewModel> _Squares;
public List<ASquareViewModel> Squares
{
get
{
if (_Squares == null)
{
_Squares = new List<ASquareViewModel>();
var colour = IsInDesignModeStatic ?
new SolidColorBrush(Color.FromArgb(0xFF, 0x78, 0xB2, 0xFF)) : // 78B2FF (pastel blue)
new SolidColorBrush(Color.FromArgb(0xFF, 0xF9, 0xFF, 0xC7)); // F9FFC7 (eggshell)
for (var i = 0; i < 10; i++)
{
_Squares.Add(new ASquareViewModel {SquareColour = colour});
}
}
return _Squares;
}
set { _Squares = value; }
}
public MainViewModel() {}
}
}
The view for this can also be edited in Expression Blend, but the view model code sets the design time colour data differently:
These are the two XAML files, firstly VMUCExample/ASquareUC.xaml:
<UserControl x:Class="VMUCExample.ASquareUC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="15" d:DesignWidth="60"
d:DataContext="{Binding ASquare, Mode=OneWay, Source={StaticResource Locator}}">
<Grid>
<Rectangle Fill="{Binding SquareColour}" Margin="2" Width="50" Height="10"/>
</Grid>
</UserControl>
You can see I've used Faster Solutions' suggestion of putting d:DataContext so that the design time binding I need for Expression Blend when I am designing the user control does not block the data context I need at run-time nor the data context supplied by the parent when I am designing the main window in Expression Blend. I am uncomfortable about this though, it is not the approach characterised as MVVM Light in Paul Stovell's Option 6: A XAML View Model Locator and endorsed by #LBugnion
The other view file is VMUCExample\MainWindow.xaml:
<Window x:Class="VMUCExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vmucExample="clr-namespace:VMUCExample"
Height="200" Width="100"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Grid x:Name="LayoutRoot">
<ScrollViewer ScrollViewer.CanContentScroll="True">
<ItemsControl ItemsSource="{Binding Squares}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<vmucExample:ASquareUC/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Window>
The example application itself just draws ten rectangles in a column:
Too simple, I know, but hopefully shows the three potential data sets in use:
Design time for the user control (#FF7878 pastel red),
Design time for the user control set by the main window (#78B2FF pastel blue), and
Run time for the user control set by the main window (#F9FFC7 eggshell).
(N.B. There is another data option, run time for the user control not set by the main window. In this case the user control's view model chooses #FFBB78/nectarine but I didn't need that to explore these bindings.)
For completeness here is the VMUCExample\App.xaml file:
<Application x:Class="VMUCExample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:VMUCExample.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
StartupUri="MainWindow.xaml"
mc:Ignorable="d">
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</Application.Resources>
</Application>

Related

Dynamically load different UserControls based on object type via data binding in xaml

Is there some way in WPF to get the same functionality DataTemplateSelector gives you, but for UserControls?
Say I have a StackView to which I want to bind an IEnumerable of objects. What I'd like to do is somehow have a mapping that, for each object type in the bound IEnumerable, looks at the object type and determines what UserControl to add to the StackView.
So, given three classes:
public class House : Building{}
public class Apartment : Building{}
public class Tent : Building{}
where each class inherits from Building and has its own defined UserControl, I'd like to set DataContext to an IEnumerable<Building> and somehow get the StackView to populate its set of children with the type-specific UserControl.
I'd like to do this with as little code behind as possible. The more data binding and XAML duct tape the better.
You can use complex user controls in a DataTemplate; just declare the DataTemplate as your UserControl.
Example:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4"
Title="MainWindow" Height="300" Width="300" Name="UI" >
<Window.Resources>
<DataTemplate DataType="{x:Type local:House}" >
<local:HouseUserControl DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Apartment}">
<local:ApartmentUserControl DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding ElementName=UI, Path=ListOfBuildings}" />
</Grid>
</Window>
I'm not sure I see the problem. Just create DataTemplates for each type in your resources somewhere and WPF will use them automatically to render each type.

WPF Expander in user control getting rendered transparent in ListBox item

I’m having issues with a WPF Expander that I have in a user control that gets rendered in a ListBox. Essentially I’m trying to get PopUpButton behavior on each ListItem in my ListBox. When I open the Expander the content is rendering behind everything else as if it were transparent, or lower in the z-order. I’ve tried this with a WPF PopUp and Toggle Button as well (using techniques described int Karle Shivllet’s blog – Expander Control with Popup Content) to no avail.
Let me first describe what it is I’m trying to do. I have two controls that display a list of inputs that I need to configure for my application. For simplicity sake, one user control is used to configure inputs to a graph, and another control is used to control inputs to a simple excel grid. The inputs for the graph and grid each have properties that need to be configured on them. I’ve developed a simple user control called InputSelectControl that will render a ListBox containing the list of inputs to be configured for the graph or grid. Each ListItem in the ListBox consist of a TextBlock for the input’s name (e.g. Pressure, ECG, etc.) and a WPF Expander that , when clicke, displays a property editor for that input. Since the property editor presentation will be different depending on whether I’m dealing with graph inputs versus grid inputs, I’ve used a DependencyProperty on my InputSelectControl that is of type ControlTemplate. This allows my grid and graph to each supply the presentation they need for editing their input properties. Also note that I will have more than just a graph and a grid that need this behavior, thus the desire to make this a user control that can dynamically receive presentation behavior.
I’ve tried placing my Expander inside my property editor template, had have also tried experimenting with the ZIndex in various places, but always end up with the same behavior, the Expander popup displays behind the ListItems in my list.
Below is some code further describing my approach. Hopefully someone can help me out of this pickle.
XAML representing my Grid (could be graph, or something else) control that hold my InputSelectControl:
<UserControl x:Class="MyApp.GridView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:props="clr-namespace:PopupButtonDependencyProp" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<!-- Specify the control tempalte we want loaded into the
properies popup for a grid-->
<ControlTemplate x:Key="GridPropertyEditorTemplate" TargetType="ContentControl">
<props:GridInputPropertyEditor />
</ControlTemplate>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Hello Grid" Margin="5" />
<!-- Tell the InputSelectControl what template to load into Property
Window for each Grid Input item -->
<props:InputSelectControl Grid.Row="1"
DataContext="{Binding VmUsedInputs, Mode=OneWay}"
PropertyEditorTemplate="{StaticResource GridPropertyEditorTemplate}" />
</Grid>
</UserControl>
XAML representing my InputSelectControl that displays my list of inputs and a ContentControl place holder for each ListItem where I want my "Popup behavior" for editing properties:
<UserControl x:Class="MyApp.InputSelectControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:props="clr-namespace:PopupButtonDependencyProp"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<!-- Listbox holding our inputs. Assuming whatever we're contained in has
set our DataContext to a valid Input collection-->
<ListBox x:Name="inputsUsed" Grid.Row="1" ItemsSource="{Binding}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionMode="Multiple" ClipToBounds="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Border x:Name="border" CornerRadius="7">
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal">
<!-- Input label-->
<TextBlock Text="{Binding Path=Label}" FontWeight ="Bold"
FontSize ="12" FontStyle = "Normal"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="5,0,5,0" />
<Expander x:Name="GridPropEditor" Header="Properties"
Height="Auto" Margin="5,0,0,0"
ToolTip="Open trace property dialog">
<!-- Properties button - The ContentControl below is rendering
the PropertyEditorTemplate that was set by whoever contains us -->
<ContentControl Template="{Binding PropertyEditorTemplate,
RelativeSource={RelativeSource AncestorType=props:InputSelectControl}}" />
</Expander>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
C# representing my DependencyProperty for injection the property editor template to present on popup.
/// <summary>
/// Interaction logic for InputSelectControl.xaml
/// </summary>
public partial class InputSelectControl : UserControl
{
#region Dependency Property stuff
/// <summary>
/// Dependency Property for control template to be rendered. This
/// lets us adorn the InputSelectControl with content in the Xaml.
/// The content can be different fore each instance of InputSelectControl.
/// </summary>
public static DependencyProperty PropertyEditorTemplateProperty =
DependencyProperty.Register("PropertyEditorTemplate",
typeof(ControlTemplate), typeof(InputSelectControl));
/// <summary>
/// PropertyEditorTemplate. This is how the property is set and get by WPF
/// </summary>
public ControlTemplate PropertyEditorTemplate
{
get { return GetValue(PropertyEditorTemplateProperty) as ControlTemplate; }
set { SetValue(PropertyEditorTemplateProperty, value); }
}
#endregion
/// <summary>
/// Constructor
/// </summary>
public InputSelectControl()
{
InitializeComponent();
}
}
XAML representing my GridInputPropertyEditor which is the template describing the presentation for editing Grid properties. This will be different for a Graph:
<UserControl x:Class="MyApp.GridInputPropertyEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Canvas Panel.ZIndex=”99”>
<!-- Property Editor Control - Assumes DataContext holds to the properties
that need to be edited-->
<StackPanel Orientation="Vertical" Background="WhiteSmoke">
<!-- Lists the properties for a Grid to be edited. We could use
any layout we need here. -->
<ListBox ItemsSource="{Binding Properties}" Background="WhiteSmoke" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5,5">
<TextBlock Text="{Binding Label}" FontWeight="Bold"/>
<TextBlock Text=":" />
<TextBox Text="{Binding Value}" Margin="10,0" Width="20" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Canvas>
</UserControl>
Using Snoop I was able to figure out that if I set the Z-Index of the ListBox item to a high number my property editor comes to the foreground. If someone sees a better way around this let me know. Otherwise I could use some help coming up with a trigger to raise and lower the zindex based on the item selected.
Okay, after a few trials and tribulations I was able to come up with solution using code behind. I'd be interested in finding a way to do this with triggers, but I'm not sure it's possible with this approach.
Here's the update expander XAML:
<Expander x:Name="GridPropEditor" Header="Properties" Height="Auto" Margin="5,0,0,0"
ToolTip="Open trace property dialog"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}}"
PreviewMouseDown="GridPropEditor_PreviewMouseDown"
Expanded="GridPropEditor_Expanded">
Here's the code behind I added to my xaml.cs file
//When an expander is expanded, collapse all the other expanders
private void GridPropEditor_Expanded(object sender, RoutedEventArgs e)
{
if (ExpandersSelected == null)
ExpandersSelected = new List<Expander>();
var expander = (sender as Expander);
var listbox = expander.Tag as ListBoxItem;
if (!ExpandersSelected.Contains(expander))
ExpandersSelected.Add(expander);
if (ExpandersSelected != null)
{
foreach(var x in ExpandersSelected)
{
if (x.Equals(expander))
{
listbox.SetValue(Panel.ZIndexProperty, 99);
continue;
}
var l = x.Tag as ListBoxItem;
x.IsExpanded = false;
l.SetValue(Panel.ZIndexProperty, 0);
}
}
}
The code behind solution closes any expanders that might already be opened and brings the currently expanding Expanders container to the foreground by setting the zindex to 99.
Again, if anyone has a better solution I'm all ears.

same two user control in xaml

I have one user control in wpf. And I want to use this user control two times in same view.
I am using mvvm approach. Can anybody give me a good suggestion that what approach I should follow.
Um, any reason why you aren't just using it twice? If you bind to the VM (as the DataContext) with the same properties, it will "just work," no differently than binding two TextBlocks (or other "regular" control) to the same property in the VM.
Here I use the same control in the same window, but it could just as easily be used in the same UserControl, DataTemplate, etc. - whatever you are defining as a "view".
XAML
<Window x:Class="MyNamespace.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace"
Title="My Window" Width="300" Height="300">
<StackPanel>
<local:MyUserControl x:Name="control1"
SomeProperty="{Binding MyMVVMProperty}" />
<local:MyUserControl x:Name="control2"
SomeProperty="{Binding MyMVVMProperty}" />
<TextBlock x:Name="txt1"
Text="{Binding AnotherMVVMProperty}" />
<TextBlock x:Name="txt2"
Text="{Binding AnotherMVVMProperty}" />
<TextBlock x:Name="txt3"
Text="{Binding AThirdMVVMProperty}" />
</StackPanel>
</Window>
Code Behind
class Window1 : Window
{
MyViewModel mViewModel = new MyViewModel();
public Window1()
{
this.DataContext = mViewModel;
}
}

Use DataTemplate in WPF with a mocked object

I have following xaml code:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding MainWindow, Source={StaticResource Locator}}">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:KeyboardViewModel}">
<vw:Keyboard />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:WelcomeViewModel}">
<vw:Welcome />
</DataTemplate>
</Window.Resources>
<DockPanel>
<DockPanel>
<ContentControl Content="{Binding Path=Workspace}" />
</DockPanel>
</DockPanel>
</Window>
When Workspace is KeyboardViewModel, then the UserControl Keyboard is shown. When Workspace is Welcome, then the Welcome screen is shown. But when I test I mock the ViewModels with Moq. Workspace then get the type IKeyboardViewModelProxyxxxxxxxxxxxxx (where xxxxxxx is a random string), that don't maps to KeyboardViewModel in the DataTemplate and WPF don't now wish DataTemplate to show.
When I use the real KeyboardViewModel, it is no problem.
Can I fix it somehow, or do I have to redesign it?
I'm having a similar issue (without using Moq however). A PARTIAL solution that I used is to inherit both KeyboardViewModel and KeyboardViewModelMock from abstract KeyboardViewModelAbstract. Then you can do:
<DataTemplate DataType="{x:Type vm:KeyboardViewModelAbstract}">
<vw:Keyboard />
</DataTemplate>
Which will work for both, the real model object and the mock.
Unfortunately this solution doesn't scale when you're dealing with models that already have a base class or have any kind of inheritance involved. I'd be great if DataTemplate could be used with interfaces, but they can't.
You can omit the DataType="{x:Type vm:KeyboardViewModel}". If you do that, it is not expecting an instance of type KeyboardViewModel to bind against anymore but only an object of any type that just has all properties that are used in the template.

"Resource with the name {Locator} cannot be found" Error when using mvvm-light user control

i am using the mvvm light toolkit to create a WPF application. I created a user control and a corresponding ViewModel. I created a ViewModel property in the ViewModelLocator. I bound the user controls datacontext to the property in the Locator class. When i edit the User Control in Blend or the VS Designer everything seems to work, since i can see my design time data.
When i now try to use my user control on the main window, which is created by the wpf template of the toolkit i receive the error "Resource with the name {Locator} cannot be found" and the row with my user control in the mainwindow.xaml is marked with a red line in Blend. In Visual Studio the same line is marked with the error: "Cannot create an instance of type MyView".
Edit:
This is the source code of the app.xaml
<Application.Resources>
<!--Global View Model Locator-->
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True">
</Application.Resources>
This is the code of the EditCustomerView.xaml
<UserControl.DataContext>
<Binding Path="EditCustomer" Source="{StaticResource Locator}" />
</UserControl.DataContext>
This is the code in my main Window
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<Binding Path="Main" Source="{StaticResource Locator}"/>
</Window.DataContext>
<Grid x:Name="LayoutRoot" Background="{DynamicResource BasicBackground}">
<Grid.RowDefinitions>
<RowDefinition Height="0.927*"/>
<RowDefinition Height="0.073*"/>
</Grid.RowDefinitions>
<ListBox Margin="4" SelectedItem="{Binding Main.SelectedCustomer, Mode=Default, Source={StaticResource Locator}}" ItemTemplate="{DynamicResource CustomerTemplate}" ItemsSource="{Binding Customers, Mode=Default}"/>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" Content="Edit" Grid.Row="1" Command="{Binding EditCustomerCommand, Mode=Default}"/>
<Border x:Name="border" Opacity="0.75" Grid.RowSpan="2" Background="#FF706F6F" BorderBrush="Black" BorderThickness="1" Visibility="{Binding EditViewVisibility, Mode=Default}">
<views:EditCustomerView HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
The application compiles and runs. The error is only occuring during design time.
Can you tell me what i am doing wrong?
Thank you in advance.
This is a known issue. Blend for some reason doesn't recognize the static global resource.
As a workaround you can create a local resource of ViewModelLocator in your Views.
<Window.Resources>
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True">
</Window.Resources>
You have to include the ViewModel namespace.
The issue is reported in codeplex here
and in stackoverflow here
Seems it is resolved in Blend 4
I've come up with a pretty nice workaround to this problem since it doesn't appear to have been fixed in Blend 4:
In the constructor for your XAML UserControl just add the resources it needs, provided you're in design mode within Blend :
public partial class OrdersControl : UserControl
{
public OrdersControl()
{
// MUST do this BEFORE InitializeComponent()
if (DesignerProperties.GetIsInDesignMode(this))
{
if (AppDomain.CurrentDomain.BaseDirectory.Contains("Blend 4"))
{
// load styles resources
ResourceDictionary rd = new ResourceDictionary();
rd.Source = new Uri(System.IO.Path.Combine(Environment.CurrentDirectory, "Resources/Styles.xaml"), UriKind.Absolute);
Resources.MergedDictionaries.Add(rd);
// load any other resources this control needs such as Converters
Resources.Add("booleanNOTConverter", new BooleanNOTConverter());
}
}
// initialize component
this.InitializeComponent();
}
There may be some edge cases, but its working OK for me in the simple cases where before I'd get a big red error symbol. I'd LOVE to see suggestions on how to better solve this problem, but this at least allows me to animate user controls that otherwise are appearing as errors.
You could also extract out the creation of resources to App.xaml.cs:
internal static void CreateStaticResourcesForDesigner(Control element)
{
if (AppDomain.CurrentDomain.BaseDirectory.Contains("Blend 4"))
{
// load styles resources
ResourceDictionary rd = new ResourceDictionary();
rd.Source = new Uri(System.IO.Path.Combine(Environment.CurrentDirectory, "Resources/Styles.xaml"), UriKind.Absolute);
element.Resources.MergedDictionaries.Add(rd);
// load any other resources this control needs
element.Resources.Add("booleanNOTConverter", new BooleanNOTConverter());
}
}
and then in the control do this BEFORE InitializeComponent():
// create local resources
if (DesignerProperties.GetIsInDesignMode(this))
{
App.CreateStaticResourcesForDesigner(this);
}
This is hard to diagnose without seeing the code, but let me take a stab at it anyway.
It sounds like the problem is the wiring up of your Locator. Make sure you have the following code in your App.xaml
<Application.Resources>
<local:NameOfMyViewModelLocatorClass x:Key="Locator" />
</Application.Resources>
This should wire everything together.
Well to remove the issue even at design time you need to include the following condition in the constructor of ViewModelLocator:
if (ViewModelBase.IsInDesignModeStatic)
{
}
else
{
//Include function to create ViewModel here like the following
CreateMain();
}
Hopefully this would resolve the issue.

Resources