F# WPF: Handling click events in ListBox - wpf

I'm trying to create a simple task scheduler using F# and WPF. It's basically just a list of tasks where every task has a 'Delete' button. Handling button clicks outside of the list is not a problem -- this can be handled with a regular command. However handling a button click in the list item is not straightforward. I tried using RelayCommand described here with binding routed to parent, but the sender object is always null (I expected it to be the task object from the collection). Also tried attaching a property as recommended here, but couldn't make it work.
How do I assign an event handler that obtains the task object with clicked Delete button?
Here is the App.fs:
namespace ToDoApp
open System
open System.Windows
open System.Collections.ObjectModel
open System.Windows.Input
open FSharp.ViewModule
open FSharp.ViewModule.Validation
open FsXaml
type App = XAML<"App.xaml">
type MainView = XAML<"MainWindow.xaml">
type Task(str) =
member x.Description with get() = str
type MainViewModel() as self =
inherit ViewModelBase()
let tasks = new ObservableCollection<Task>()
let addTaskCommand() =
let descr = sprintf "Do something at %A" (DateTime.Now.AddMinutes(30.0))
tasks.Add <| new Task(descr)
member this.Tasks with get() = tasks
member this.AddTask = this.Factory.CommandSync addTaskCommand
module main =
[<STAThread>]
[<EntryPoint>]
let main argv =
App().Run()
MainWindow.xaml:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ToDoApp;assembly=ToDoApp"
xmlns:fsxaml="http://github.com/fsprojects/FsXaml"
Title="Simple ToDo app" Height="200" Width="400">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Name="newJobButton" Command="{Binding AddTask}" Width="100" Height="32" Margin="5, 5, 5, 5" HorizontalAlignment="Left">New task</Button>
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ListBox Name="lstBox" ItemsSource="{Binding Tasks}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Description}" Margin="5 5 0 0"/>
<!-- OnClick ??? -->
<Button Grid.Column="1">Delete</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</Grid>
</Window>
App.xaml is trivial, so I'm not showing it here.

It's better to stick to Commands:
ViewModel
member __.DelTask =
__.Factory.CommandSyncParam
(fun task -> tasks.Remove task |> ignore)
XAML
<Button Grid.Column="1"
Command="{Binding DataContext.DelTask, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding}"
>Delete</Button>
Using event handlers in XAML results in spaghetti code which is harder to test and maintain (i.e. separation of concerns, handling business logic problems separately from UI problems).

Related

Capturing name of StackPanel from Drop event

Within WPF I have the following XAML code:
<Page x:Class="com.MyCo.MyProj.Pages.Configuration.ManageLinkage.MasterLinkage"
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:local="clr-namespace:com.MyCo.MyProj.Pages.Configuration.ManageLinkage"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="MasterLinkage">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TabControl TabStripPlacement="Top" Background="Transparent">
<TabItem Header="Import">
<ListBox Margin="0,5,0,0" Name="lbxImportItems" HorizontalAlignment="Left" VerticalAlignment="Top" Width="110" Background="Transparent"
PreviewMouseLeftButtonDown="lbxImportItems_PreviewMouseLeftButtonDown" >
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Name="DBImport">
<Image Source="/Images/DBImport25px.png" VerticalAlignment="Center" HorizontalAlignment="Center"></Image>
<TextBlock Text="Database" Foreground="AntiqueWhite"/>
</StackPanel>
<StackPanel Orientation="Vertical" Name="CSVImport">
<Image Source="/Images/CSVImport25px.png" VerticalAlignment="Center" HorizontalAlignment="Center"></Image>
<TextBlock Text="CSV Import" Foreground="AntiqueWhite"/>
</StackPanel>
</ListBox>
</TabItem>
</TabControl>
<Canvas x:Name="cnvsLinkScreen" AllowDrop="True" Grid.Column="1" Background="Transparent" Drop="cnvsLinkScreen_Drop" DragOver="cnvsLinkScreen_DragOver" ></Canvas>
</Grid>
The code for capturing the event is here:
private void cnvsLinkScreen_Drop(object sender, DragEventArgs e)
{
Canvas parent = (Canvas)sender;
object data = e.Data.GetData(typeof(string));
StackPanel objIn = (StackPanel)e.Data;
...
}
The drag and drop work great, the event method created the image in the canvas. However, I want to capture the Name="" from the StackPanels which are dropped.
I found the Name buried super deep in the "DragEventArgs e" object. I was think that there should be a way to cast the object (or the object within that object) as a StackPanel to easily work with it. The above code does not convert the StackPanel object( it's not at the root or the child object; I tried both) so it exceptions on "StackPanel objIn = (StackPanel)e.data;"
How do I either translate the incoming object to a StackPanel or how do I access the Name attribute from the Stackpanel?
I got it. I was close with the translation. To translate / typecast the object to what you are working with I needed to use the following line:
StackPanel objIn = (StackPanel)(e.Data.GetData(typeof(StackPanel)));
Which is slightly different than above.

access codebehind method from resource dictionary in WPF

I have following grid inside WPF XML file
<Grid Grid.RowSpan="2" >
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height = "Auto" />
</Grid.RowDefinitions >
<telerik:RadAutoCompleteBox TextSearchMode = "Contains" Grid.Column= "0" Grid.Row= "0" Name="cmbStyleNo" Margin= "5"
DisplayMemberPath="SAMPLE_ID" ItemsSource="{Binding Styles}"
SelectionMode= "Single" AutoCompleteMode= "Suggest" NoResultsContent= "No Matches" SelectionChanged= "val_SelectionChanged" />
</Grid >
above RadAutoCompleteBox SelectionChanged="val_SelectionChanged" code behind method exist in its code-behind file like follows
private void val_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
...
}
Then I have requirement populate above same grid in another window using resource dictionary. so I copied that grid to that ResourceDictionary like follows
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:samplePrjkt"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation">
<ToolBar x:Key="MyToolbar" Height="120">
<!--Template-->
<GroupBox Header="Template" Style="{StaticResource ToolbarGroup}" Margin="3">
<StackPanel Grid.Row="1" Orientation="Horizontal">
<StackPanel Orientation="Vertical" Margin="0,2,0,2">
<Grid Grid.RowSpan="2" >
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<telerik:RadAutoCompleteBox TextSearchMode="Contains" Grid.Column="0" Grid.Row="0" Name="cmbStyleNo" Margin="5"
DisplayMemberPath="SAMPLE_ID" ItemsSource="{Binding Styles}"
SelectionMode="Single" AutoCompleteMode="Suggest" NoResultsContent="No Matches" SelectionChanged="val_SelectionChanged"/>
</Grid>
</StackPanel>
</StackPanel>
</GroupBox>
</ToolBar>
</ResourceDictionary>
then once I compile this I'm getting following error
'ResourceDictionary' root element requires a x:Class attribute to
support event handlers in the XAML file. Either remove the event
handler for the SelectionChanged event, or add a x:Class attribute to
the root element.
You need to manually add a code-behind file for the ResourceDictionary and define the val_SelectionChanged event handler in this file.
Please refer to the following link for more information and an example of how to do this:
Is it possible to set code behind a resource dictionary in WPF for event handling?
You basically just create a new class and name it "Dictionary1.xaml.cs" where "Dictionary1" is the name of your resource dictionary XAML file and then you set the x:Class attribute in the XAML file:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication1.Dictionary1"
...

Caliburn Micro Message.attach does not consider changes of datacontext

This is a simplified example. I have a usercontrol that contains a "browse to folder" functionality, using a textbox and a button. Clicking the button would open up the browse-dialog, and would essentially fill in the textbox.
<UserControl x:Class="MyUserControl"
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:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<!-- Folder -->
<TextBlock>Path</TextBlock>
<DockPanel LastChildFill="True" Grid.Column="1">
<Button DockPanel.Dock="Right" cal:Message.Attach="[Event Click] = [Action BrowseHotFolder()]" Content="..." HorizontalAlignment="Left" Width="25" Height="25" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Margin="0,0,5,0"/>
<TextBox Text="{Binding HotFolderPath, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
</DockPanel>
</Grid>
I have a listbox that contains a number of objects. The selected object will be fed into this usercontrol as datacontext.
<Window>
...
<Listbox ItemsSource="{Binding Items, Mode=OneWay}" SelectedItem="{Binding SelectedItem}">
...
<view:MyUserControl DataContext="{Binding SelectedItem}" />
</Window>
Now, let's say I have two items in my listbox and I have the first one selected. I fill in "foo" in the textbox of MyUserControl. Then I select the second item, and fill in "bar". The databinding works fine, and both items have the correct values set. If I then click the browse button on the first one and select a folder, it will change the textbox of the first item to the selected path. However, if I select the second item, and browse to a folder it will ALSO change the first item's textbox.
My guess is that the message attach syntax does not call the browse action on the correct Item. It disregards the datacontext (currently selected item) and just uses the first one.
What can I do about this?
I think your guess is correct; the target used for the Message.Attach is the first data context bound, and does not update when the context is changed after the user selection.
We saw a similar problem with user controls switched in a content control - the fix was to specify cal:Action.TargetWithoutContext="{Binding}" on the button.
The issue is mentioned here by Rob Eisenberg:
https://caliburnmicro.codeplex.com/discussions/257005
I have made a workaround by changing
cal:Message.Attach="[Event Click] = [Action BrowseHotFolder()]"
to
cal:Message.Attach="[Event Click] = [Action BrowseHotFolder($datacontext)]"
Now, the BrowseHotFolder-method still is called on the wrong ItemViewModel, but weirdly $datacontext passes the correct ItemViewModel. In the method itself I now do:
public void BrowsePath(ItemViewModel context)
{
context.Path = _folderBrowsingService.Browse();
}
This is a workaround, but solves the problem.

ListBox with customized Item in VB.NET

I want to modify the original ListBox control that the each item to have a CheckBox, Labels and a Button control inside.
Is there any optimal method to make that? without making Custom Control from the very beginning?
Making custom control that inherits ListBox could be not a bad idea, but don't know how...
Thank you!
I tried WPF but it was too difficult at this time. Actually, designing the control via XAML was easy, but managing the list items(add/delete with texts, get event from the button in each item) wasn't.
Since the question is tagged [WPF] I'm going to provide a WPF answer:
The first thing any developer who faces WPF immediately tries to do is to use it as if it were winforms. This is a big mistake.
If you're working with WPF, you really need to leave behind the traditional aproach used in archaic technologies such as winforms, and understand and embrace The WPF Mentality.
in WPF, you don't "add/delete with texts, get event from the button in each item" or any of that, simply because UI is not Data.
Instead, you define a simple Data Model:
public class MyData
{
public string MyText1 {get;set;}
public string MyText2 {get;set;}
}
and then declaratively define Data Bindings in the UI to "show" this data to the UI as opposed to "reading" or "writing" data to/from the UI:
<Window x:Class="WpfApplication14.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication14"
Title="MainWindow" Height="350" Width="525">
<ListBox ItemsSource="{Binding}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="5" Background="LightCyan" BorderBrush="LightSkyBlue" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Text 1:" HorizontalAlignment="Right"/>
<Label Grid.Row="1" Grid.Column="0" Content="Text 2:" HorizontalAlignment="Right"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding MyText1}"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding MyText2}"/>
<Button Grid.Row="2" Grid.ColumnSpan="2" Content="Button" HorizontalAlignment="Center"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
And finally, you define the DataContext of the Window or View to a relevant instance or collection of such data:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = Enumerable.Range(0,10)
.Select(x => new MyData()
{
MyText1 = "Text1 - " + x.ToString(),
MyText2 = "Text2 - " + x.ToString()
});
}
}
All this results in:
See? really simple and beautiful.
Forget winforms, WPF Rocks. Just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.
The best way to do this, short of using WPF, is to create a custom UserControl which represents each item that will go in the list. Then, add a FlowLayoutPanel to your form. Set the panel's AutoScroll property to True. Then set its FlowDirection property to TopToBottom. Then, dynamically create one of your custom controls for each item that you need to add to the list and call the panel's Controls.Add method to add them to the list.

WPF staticresource references to Logical Resources in DataTemplates not resolving at runtime

Have I missed something in the step up from .net 3.5 to .net 4 because I'm seeing seemingly buggy behaviour that seems contrary to the goal of the system.
I am trying to drum up a simple MVVM library for work using a few examples. I am consuming it within a Twitter client application for some additional learning and have hit a big stumbling block.
The scenario is this. My root ViewModel (TwitterClientViewModel) object is given an instance of a DialogViewModel object for display. The DialogViewModel is added to a collection and a bool HasDialogs is set to true. PropertyChanged events are invoked for the collection and the flag if necessary. This part works fabulously.
The view for TwitterClientViewModel is called TwitterClientTemplate and makes Visible an overlay for DialogViewTemplate (DialogViewModel's view) hosting. The hosting ContentControl's template references DialogViewTemplate with a DynamicResource extension. This displays great in the designer and at runtime.
This is where things get strange. The 'body' of DialogViewTemplate hosts dialog content with a further content control bound to DialogViewModel.Content (type object). The hope was that with use of a TemplateSelector (of which I wrote a nice declarative one, but have commented out for testing purposes) I could display both text and interactive elements. For example, requesting details from the user when authenticating a Twitter account. In this case, a PIN number.
At this point I have a two nested contentcontrols for the dialog implementation. For testing purposes, the contentcontrol in the body of DialogViewTemplate uses a staticresource extension to retrieve EnterPINDialogTemplate (view for EnterPINDialogViewModel). Both EnterPINDialogTemplate and DialogViewTemplate are in the same file (the former is defined first of course) although originally they were separate.
At runtime the staticresource extension throws a XamlParseException with the message;
'Provide value on 'System.Windows.Markup.StaticResourceHolder' threw an exception.'
and an inner exception message;
'Cannot find resource named 'EnterPINDialogTemplate'. Resource names are case sensitive'
Using a dynamicresource returns null and displays the Fullname of the EnterPINDialogViewModel type in the contentcontrol - as expected when the resource is not resolved. Breaking into my custom TemplateSelector as calling FrameWorkElement.FindResource() throws a similar exception (TryFindResource returns null).
My first thought was that the logical tree is split when the datatemplates are constructed and I remembered an issue in that area from an earlier project. I tried using the MergeDictionaries property of ResourceDictionary to make the resource dictionaries avaliable from within the DataTemplate but the designer did not like that one bit, and the error is described here:
http://connect.microsoft.com/VisualStudio/feedback/details/498844/wpf-designer-throws-invalidcastexception
Scratch that idea. I have tried merging the dictionaries at Application, Window and TwitterClientTemplate levels but have had no luck.
Below are the xaml files.
DialogTemplates.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VM="clr-namespace:EpicTweet.ViewModel"
xmlns:ET="clr-namespace:EpicTweet"
xmlns:T="clr-namespace:EpicTweet.Tools"
xmlns:MV="clr-namespace:MVVM;assembly=MVVM"
xmlns:Loc="clr-namespace:EpicTweet.Localization"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<DataTemplate DataType="VM:EnterPINDialogViewModel" x:Key="EnterPINDialogTemplate">
<Grid d:DesignWidth="453.89" d:DesignHeight="78.92" Loc:ResXManagerProperty.ResourceManager="{x:Static ET:Language.ResourceManager}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="{Loc:ResxExtension ResourceName=String_PIN, FallbackValue='<PIN>'}"/>
<TextBox Grid.Column="1"/>
<TextBlock Grid.Row="1" Grid.RowSpan="2"></TextBlock>
</Grid>
</DataTemplate>
<DataTemplate x:Key="DialogViewTemplate" DataType="MV:DialogViewModel">
<Border BorderBrush="Black" BorderThickness="1">
<Grid d:DesignWidth="277.419" d:DesignHeight="74.96" Background="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" Height="Auto" Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border d:LayoutOverrides="Width, Height" BorderThickness="0,0,0,1" BorderBrush="Black">
<Label Content="{Binding DisplayName, FallbackValue=Header}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
</Border>
<ContentControl Content="{Binding Content, FallbackValue=Body}" ContentTemplate="{StaticResource EnterPINDialogTemplate}" HorizontalAlignment="Stretch" d:LayoutOverrides="Height" Grid.Row="1" Margin="5">
<!--<ContentControl.ContentTemplateSelector>
<T:TypeTemplateSelector>
<T:TemplateTypeRelationship Type="{x:Type VM:EnterPINDialogViewModel}" ResourceKey="EnterPINDialogTemplate"/>
</T:TypeTemplateSelector>
</ContentControl.ContentTemplateSelector>-->
</ContentControl>
<ItemsControl Grid.Row="2" Margin="10"
ItemsSource="{Binding Commands, Mode=OneTime, FallbackValue={x:Static VM:TwitterClientViewModel.DEFAULT_DIALOG_COMMANDS}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Content="{Binding DisplayName, FallbackValue=CommandName, Mode=OneWay}"
Command="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</Border>
</DataTemplate>
TwitterClientDataTemplate.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VM="clr-namespace:EpicTweet.ViewModel"
xmlns:ET="clr-namespace:EpicTweet"
xmlns:MV="clr-namespace:MVVM;assembly=MVVM"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DialogTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<DataTemplate x:Key="TwitterClientTemplate" DataType="MV:TwitterClientViewModel">
<ScrollViewer d:DesignWidth="285.083" d:DesignHeight="119.96">
<Grid>
<StackPanel d:LayoutOverrides="Width, Height">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding AddAccountCommand.Command}" Content="{Binding AddAccountCommand.DisplayName, FallbackValue=<Add Account>}"/>
</StackPanel>
<ContentControl/>
</StackPanel>
<Border BorderThickness="1" Background="#80000000" Visibility="{Binding HasDialogs, Converter={StaticResource BooleanToVisibilityConverter}, FallbackValue=Collapsed, Mode=OneWay}">
<Grid VerticalAlignment="Stretch" MinWidth="50" MaxWidth="200">
<ContentControl Content="{Binding Dialogs[0], Mode=OneWay}" ContentTemplate="{DynamicResource DialogViewTemplate}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
</Grid>
</ScrollViewer>
</DataTemplate>
Help me stackoverflow, you're my only hope!
EDIT: Did some further work on this issue. If both templates are in the same file dynamicresource and staticresource extensions both resolve the resource without issue. If they are in separate files, the resource will not resolve regardless of how I merge the dictionaries; each extension returns null.
Obviously the solution is to throw both resources into the same dictionary but as far as I'm concerned this is a hack and is not intended behaviour of the logical resource lookup system. I am not a happy bunny right now. This seems pretty undocumented...
If ever there was a pratt, it is me. After 4 hours on a Friday night trying to solve this one I have cracked it, no thanks to what I can only call flaky error reporting.
Here is the buzz.
<DataTemplate x:Key="TwitterClientTemplate" DataType="MV:TwitterClientViewModel">
should be
<DataTemplate x:Key="TwitterClientTemplate" DataType="{x:Type MV:TwitterClientViewModel}">
And bam, it works.
My big gripe remains however. Why does the incorrect syntax work in the designer but not at runtime? My guess is because runtime optimization just doesn't bother to populate a dictionary with poorly authored xaml but it would be nice to get a warning that it is wrong.

Resources