Error Implementing Design Time Data Template - wpf

I want to display dummy data in the design window of a WPF GridView and found a similar example: Answer: Design time data for datatemplate in xaml - Stack Overflow
I created a new WPF App (.NET Framework 4.6.1) named WpfDataTemplate and copied in the code snippets from the example into MainWindow.xaml.cs and MainWindow.xaml. All additions are noted in comments with ##.
Unexpected Behavior:
The line <local:MyMockClass x:Key="DesignViewModel" /> produces the error "Object reference not set to an instance of an object."
The test text # strings are not displayed in the Designer.
The test text # strings are not displayed in the Window at runtime.
Questions:
What is the cause of the error?
What do I need to change for the code to work as expected?
MainWindow.xaml
<Window x:Class="WpfDataTemplate.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:local="clr-namespace:WpfDataTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<!-- ## BEGIN ADD -->
<Window.Resources>
<local:MyMockClass x:Key="DesignViewModel" />
</Window.Resources>
<!-- ## END ADD -->
<Grid>
<!-- ## BEGIN ADD -->
<ListBox x:Name="standardLayoutListBox"
d:DataContext="{Binding Source={StaticResource DesignViewModel}}"
ItemsSource="{Binding MyListBoxItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Column="0" Content="{Binding text1}" />
<Label Grid.Column="1" Content="{Binding text2}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- ## END ADD -->
</Grid>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
// ## BEGIN ADD
using System.Collections.ObjectModel;
// ## END ADD
namespace WpfDataTemplate
{
// ## BEGIN ADD
public class MyMockClass
{
public MyMockClass()
{
MyListBoxItems.Add(new MyDataClass() { text1 = "test text 1", text2 = "test text 2" });
MyListBoxItems.Add(new MyDataClass() { text1 = "test text 3", text2 = "test text 4" });
}
public ObservableCollection<MyDataClass> MyListBoxItems { get; set; }
}
public class MyDataClass
{
public string text1 { get; set; }
public string text2 { get; set; }
}
// ## END ADD
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}

Firstly you need to initialise your collection in your MyMockClass.
public class MyMockClass
{
public MyMockClass()
{
MyListBoxItems = new ObservableCollection<MyDataClass>()
MyListBoxItems.Add(new MyDataClass() { text1 = "test text 1", text2 = "test text 2" });
MyListBoxItems.Add(new MyDataClass() { text1 = "test text 3", text2 = "test text 4" });
}
public ObservableCollection<MyDataClass> MyListBoxItems { get; set; }
}
I use the design instance in my Window like so:
d:DataContext="{d:DesignInstance Type=local:MyMockClass, IsDesignTimeCreatable=True}"

Related

Sort ObservableCollection according to its property

I know this has been asked before, but still can't wrap my head around any solution that will work for my case. I want to be able to sort the observable collection in this project according to the ItemName. here is the full project https://gist.github.com/NewCoderNotInTown/322274bd7d2fd57bf2ae7784e1315b73.
the aim is to have both lists, the original one and the copied one, sorted alphabetically.
appreciate a solution for this case. as simple as possible for a beginner.
I Edit the question with complete code as advised.
Code for MainWindow.xaml
<Window x:Class="TwoWindows.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:WB="clr-namespace:TwoWindows"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Button Grid.Row="1" Content="Click Me" Width="80" Height="25" Click="ClickMe_Click"/>
<TabControl Grid.Row="0" Margin="10">
<TabItem>
<TabItem.Header>
<StackPanel>
<TextBlock Text="WindowB"/>
</StackPanel>
</TabItem.Header>
<WB:WindowB x:Name="_windowB"/>
</TabItem>
</TabControl>
</Grid>
Code for MainWindow.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TwoWindows
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ClickMe_Click(object sender, RoutedEventArgs e)
{
var windowBViewModel = new WindowBViewModel("WindowB");
var windowB = new WindowB();
var windowAViewModel = new WindowAViewModel("WindowA", windowBViewModel);
var windowA = new WindowA();
windowA.DataContext = windowAViewModel;
windowA.Show();
_windowB.DataContext = windowBViewModel;
}
}
}
Code for WindowA.xaml
<Window x:Class="TwoWindows.WindowA"
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:local="clr-namespace:TwoWindows"
mc:Ignorable="d"
Title="WindowA" Height="300" Width="600">
<Grid>
<Label Content="{Binding PageTitle}" />
<ListBox ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding ItemName}" IsChecked="{Binding IsChecked}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Code for WindowA.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace TwoWindows
{
public partial class WindowA : Window
{
public WindowA()
{
InitializeComponent();
}
}
}
Code for WindowB.xaml
<UserControl x:Class="TwoWindows.WindowB"
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:local="clr-namespace:TwoWindows"
mc:Ignorable="d">
<Grid>
<Label Content="{Binding PageTitle}" />
<ListBox ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ItemName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
Code for WindowB.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace TwoWindows
{
public partial class WindowB : UserControl
{
public WindowB()
{
InitializeComponent();
}
}
}
Code for WindowAViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoWindows
{
public class WindowAViewModel : BaseWindowViewModel
{
public WindowAViewModel(string pageTitle, WindowBViewModel windowBViewModel)
{
PageTitle = pageTitle;
MyItems.Add(new MyCustomItemViewModel("Apple", windowBViewModel));
MyItems.Add(new MyCustomItemViewModel("Orange", windowBViewModel));
MyItems.Add(new MyCustomItemViewModel("Banana", windowBViewModel));
}
}
}
Code for WindowBViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoWindows
{
public class WindowBViewModel : BaseWindowViewModel
{
public WindowBViewModel(string pageTitle)
{
PageTitle = pageTitle;
}
}
}
Code for MyCustomItemViewModel.cs
using PropertyChanged;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoWindows
{
[ImplementPropertyChanged]
public class MyCustomItemViewModel
{
public string ItemName { get; set; }
public bool IsChecked { get; set; }
public WindowBViewModel WindowBViewModelObj { get; set; }
public MyCustomItemViewModel(string itemName, WindowBViewModel windowBViewModel)
{
ItemName = itemName;
WindowBViewModelObj = windowBViewModel;
}
private void OnIsCheckedChanged()
{
if (IsChecked)
WindowBViewModelObj.MyItems.Add(this);
else
WindowBViewModelObj.MyItems.Remove(this);
}
}
}
Code for BaseWindowViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoWindows
{
public class BaseWindowViewModel
{
public string PageTitle { get; set; }
public ObservableCollection<MyCustomItemViewModel> MyItems { get; } = new ObservableCollection<MyCustomItemViewModel>();
}
}
Here's three common ways for sorting from your code.
Manually apply the Sort anytime you add items. I would suggest linq for the actual sorting, although you can build your own. In your specific case, it would look like this :
MyItems.Add(new MyCustomItemViewModel("Apple", windowBViewModel));
MyItems.Add(new MyCustomItemViewModel("Orange", windowBViewModel));
MyItems.Add(new MyCustomItemViewModel("Banana", windowBViewModel));
MyItems = MyItems.Sort(p => p.ItemName);
This assumes you enable the setter so MyItems is not readonly.
In the getter for MyItems, return the collection sorted (and use a backing property for it). This is not really ideal, since you build the sorted collection for each call that is made to the getter. For your code, it would look like this :
public class BaseWindowViewModel
{
public string PageTitle { get; set; }
private ObservableCollection<MyCustomItemViewModel> _myItems = new ObservableCollection<MyCustomItemViewModel>()
public ObservableCollection<MyCustomItemViewModel> MyItems
{
get { return _myItems.Sort(p => p.ItemName); }
}
}
(Recommended) Work with a CollectionViewSource that sits on top of your collection and applies UI-friendly operations like sorting and filtering.
public class BaseWindowViewModel
{
public string PageTitle { get; set; }
private ObservableCollection<MyCustomItemViewModel> _myItems;
public ObservableCollection<MyCustomItemViewModel> MyItems
{
get
{
if (_myItems == null)
{
_myItems = new ObservableCollection<MyCustomItemViewModel>();
_myItemsSorted = CollectionViewSource.GetDefaultView(_myItems)
_myItemsSorted.SortDescriptions.Add(new SortDescription() { PropertyName = "ItemName" });
}
return _myItems;
}
}
private ICollectionView _myItemsSorted;
public ICollectionView MyItemsSorted { get { return _myItemsSorted; }}
}
And then just bind to MyItemsSorted rather than MyItems
You can do this using LINQ, but you'll have to call Sort() when you're done adding or removing items. If that happens a lot with large lists, you may want to look into ObservableCollection.Insert and using your own comparer.
public class BaseWindowViewModel
{
public string PageTitle { get; set; }
public ObservableCollection<MyCustomItemViewModel> MyItems
{
get
{
return _MyItems;
}
}
private ObservableCollection<MyCustomItemViewModel> _MyItems = new ObservableCollection<MyCustomItemViewModel>();
public void Sort()
{
_MyItems = new ObservableCollection<MyCustomItemViewModel>(from i in _MyItems orderby i.ItemName select i);
}
}

WPF wrappanel how to pass data to a usercontrol

Im not 100% this goes with the whole wpf mvvm design pattern but I'm lost on how to do it.
I have a main view in this main view i have ObservableCollection called 'DemoClass_List', each element in the list contains a class 'DemoClass'.
In the wpf for this view i have wrappanel, what i want is for the wrappanel to display a usercontrol for each element, but also to pass the individual class to the element.
Code is below.
***UserControl
public partial class DemoClass_Display : UserControl
{
public DemoClass _demoClass;
public DemoClass_Display()
{
InitialiseComponent();
//Do processing on _demoClass and display relevant info
}
}
***MainView WPF Snippet
<ItemsControl ItemsSource = "{Binding DemoClass_List}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<usercontrol:DemoClass_Display/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The question is, how do i fill the item "public DemoClass _demoClass;" in the usercontrol?
****************Edit****************
This code crashes
****MainView
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public static ObservableCollection<DemoClass> DemoClass_List { get; set; }
public Window1()
{
DemoClass_List = new ObservableCollection<DemoClass>();
DemoClass dc1 = new DemoClass();
dc1.Property1 = 1;
dc1.Property2 = 1;
dc1.Property3 = 1;
DemoClass dc2 = new DemoClass();
dc2.Property1 = 2;
dc2.Property2 = 2;
dc2.Property3 = 2;
DemoClass dc3 = new DemoClass();
dc3.Property1 = 3;
dc3.Property2 = 3;
dc3.Property3 = 3;
DemoClass_List.Add(dc1);
DemoClass_List.Add(dc2);
DemoClass_List.Add(dc3);
InitializeComponent();
}
}
}
***MainViewWPF
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UserControl="clr-namespace:WpfApplication1"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="Window1" Height="300" Width="300">
<Grid>
<ItemsControl ItemsSource="{Binding DemoClass_List}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<UserControl:DemoClass_Display demoClass="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
***UserControl
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for DemoClass_Display.xaml
/// </summary>
public partial class DemoClass_Display : UserControl
{
public static readonly DependencyProperty DemoClassProperty = DependencyProperty.Register("demoClass", typeof (DemoClass), typeof (DemoClass_Display), new PropertyMetadata(default(DemoClass)));
public DemoClass demoClass
{
get { return (DemoClass)GetValue(DemoClassProperty); }
set { SetValue(DemoClassProperty, value); }
}
public DemoClass_Display()
{
InitializeComponent();
Console.WriteLine(demoClass.Property1);
}
}
}
The console crashes as demoClass hasnt been set
You need to create a dependency property in your UserControl:
public static readonly DependencyProperty DemoClassProperty = DependencyProperty.Register(
"DemoClass", typeof (DemoClass), typeof (DemoClass_Display), new PropertyMetadata(default(DemoClass), OnDemoClassChanged));
public DemoClass DemoClass
{
get { return (DemoClass) GetValue(DemoClassProperty); }
set { SetValue(DemoClassProperty, value); }
}
private static void OnDemoClassChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var newDemoClass = e.NewValue as DemoClass;
var demoClassDisplay = (DemoClass_Display)dependencyObject; // you can access this UserControl this way.
if(newDemoClass != null)
{
//Do processing on newDemoClass and display relevant info
}
}
And then you can bind the current item of the ItemsControl in the DataTemplate:
<ItemsControl.ItemTemplate>
<DataTemplate>
<usercontrol:DemoClass_Display DemoClass="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
So inside the UserControl you will use the DemoClass property instead of the field _demoClass. I think that can even be deleted.

The name 'InitializeComponent' does not exist in the current context : strange behaviour

I have created very small WPF application and facing one problem. I have below classes.
Employee.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace StaticResourceVsDynamicResource
{
public class Employee
{
public string strName;
public int nId;
public Employee()
{
strName = "Default name";
nId = -1;
}
public string Name
{
get{return strName;}set{strName = value;}
}
public int ID
{
get{return nId;}set{nId = value;}
}
}
}
MainWindow.xamal.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace StaticResourceVsDynamicResource
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
this.Resources["objEmployee"] = new Employee { Name = "Changed employee", ID = 100};
this.Resources.Add("myBrush",new SolidColorBrush(SystemColors.GrayTextColor));
}
}
}
MainWindow.xamal
<Window x:Class="StaticResourceVsDynamicResource.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StaticResourceVsDynamicResource"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<x:ArrayExtension Type="{x:Type sys:String}" x:Key="objNames">
<sys:String>A1</sys:String>
<sys:String>A2</sys:String>
</x:ArrayExtension>
<local:Employee x:Key="objEmployee"></local:Employee>
</Window.Resources>
<Grid>
<Grid Height="100" HorizontalAlignment="Left" Margin="281,12,0,0" Name="grid3" VerticalAlignment="Top" Width="200" >
<ComboBox ItemsSource="{StaticResource ResourceKey=objNames}" Height="23" HorizontalAlignment="Left" Margin="48,37,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</Grid>
Above xaml code is intresting. When I build this code I didn't get any error. For whatever reason I just shuffle position of <x:ArrayExtension> and <local:Employee> and I start getting below error.
The name 'InitializeComponent' does not exist in the current context
When I am declaring <local:Employee> before <x:ArrayExtenion> then only I am getting this error. I am sure this has to do something with namespace, but I am not able to figure it out. See the below code which is causing compilation error.
<Window.Resources>
<local:Employee x:Key="objEmployee"></local:Employee>
<x:ArrayExtension Type="{x:Type sys:String}" x:Key="objNames">
<sys:String>A1</sys:String>
<sys:String>A2</sys:String>
</x:ArrayExtension>
</Window.Resources>
Can anyone help? Seems to be a strange problem but it is...
Regards,
Hemant
I've had the same problem. The way I resolved it was by changing the build action of the XAML file to Page.
To credit the source where I found the solution:
http://blog.mahop.net/post/Compile-Error-for-WPF-Files-The-name-InitializeComponent-does-not-exist-in-the-current-context.aspx

How to highlight matching sub-strings inside a ListBox?

I have one TextBox and one listbox for searching a collection of data. While searching a text inside a Listbox if that matching string is found anywhere in the list it should show in Green color with Bold.
eg. I have string collection like
"Dependency Property, Custom Property, Normal Property". If I type in the Search Text box "prop" all the Three with "prop" (only the word Prop) should be in Bold and its color should be in green. Any idea how it can be done?.
Data inside listbox is represented using DataTemplate.
I've created a HighlightTextBehavior that you can attach to a TextBlock within your list item templates (you'll need to add a reference to System.Windows.Interactivity to your project). You bind the behavior to a property containing the text you want to highlight, and it does the rest.
At the moment, it only highlights the first instance of the string. It also assumes that there is no other formatting applied to the text.
using System.Linq;
using System.Text;
using System.Windows.Interactivity;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
namespace StringHighlight
{
public class HighlightTextBehavior : Behavior<TextBlock>
{
public string HighlightedText
{
get { return (string)GetValue(HighlightedTextProperty); }
set { SetValue(HighlightedTextProperty, value); }
}
// Using a DependencyProperty as the backing store for HighlightedText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HighlightedTextProperty =
DependencyProperty.Register("HighlightedText", typeof(string), typeof(HighlightTextBehavior), new UIPropertyMetadata(string.Empty, HandlePropertyChanged));
private static void HandlePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
(sender as HighlightTextBehavior).HandlePropertyChanged();
}
private void HandlePropertyChanged()
{
if (AssociatedObject == null)
{
return;
}
var allText = GetCompleteText();
AssociatedObject.Inlines.Clear();
var indexOfHighlightString = allText.IndexOf(HighlightedText);
if (indexOfHighlightString < 0)
{
AssociatedObject.Inlines.Add(allText);
}
else
{
AssociatedObject.Inlines.Add(allText.Substring(0, indexOfHighlightString));
AssociatedObject.Inlines.Add(new Run() {
Text = allText.Substring(indexOfHighlightString, HighlightedText.Length),
Foreground = Brushes.Green,
FontWeight = FontWeights.Bold });
AssociatedObject.Inlines.Add(allText.Substring(indexOfHighlightString + HighlightedText.Length));
}
}
private string GetCompleteText()
{
var allText = AssociatedObject.Inlines.OfType<Run>().Aggregate(new StringBuilder(), (sb, run) => sb.Append(run.Text), sb => sb.ToString());
return allText;
}
}
}
Here's an example of how you use it:
<Window x:Class="StringHighlight.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:b="clr-namespace:StringHighlight"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<x:Array x:Key="MyStrings" Type="{x:Type sys:String}">
<sys:String>This is my first string</sys:String>
<sys:String>Another string</sys:String>
<sys:String>A third string, equally imaginative</sys:String>
</x:Array>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox x:Name="SearchText"/>
<ListBox Grid.Row="1" ItemsSource="{StaticResource MyStrings}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Grid.Row="1" Text="{Binding}">
<i:Interaction.Behaviors>
<b:HighlightTextBehavior HighlightedText="{Binding ElementName=SearchText, Path=Text}"/>
</i:Interaction.Behaviors>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>

WPF DataTemplate not throwing exception

While designing a new WPF application I noticed exceptions not being thrown during data binding for controls with DataTemplates. To test I wrote the following simple user control with as little logic as possible. I'm using .NET 3.5 SP1, VS2008 SP1 running on WIN7.
When the DataContext is set, TestMethod is called and an exception is thrown in the codebehind but the application doesn't break.
Would someone mind explaining what is happening when the DataTemplate is being created? Specifically I'm interested in where the exception goes.
Below is the XAML code:
<UserControl x:Class="WPF2008.DataTemplateQuestion"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<UserControl.Resources>
<DataTemplate x:Key="TESTKey">
<Border BorderBrush="Black" BorderThickness="10">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding Path=Txt}" />
<TextBox Grid.Row="1" Text="{Binding Path=Lbl}" />
<TextBox Grid.Row="2" Text="Am I disabled?" />
<TextBox Grid.Row="3" Text="I am disabled." IsEnabled="False" />
</Grid>
</Border>
</DataTemplate>
</UserControl.Resources>
<Grid>
<Label x:Name="lblTest" Content="{Binding Path=TestMethod}" ContentTemplate="{StaticResource TESTKey}" />
</Grid>
</UserControl>
Below is the codebehind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WPF2008
{
public partial class DataTemplateQuestion : UserControl
{
public DataTemplateQuestion()
{
try
{
InitializeComponent();
lblTest.DataContext = new TestClass();
}
catch (Exception e)
{
// ADDING A BREAKPOINT HERE DOESN'T CATCH THE EXCEPTION
throw e;
}
}
}
public class TestClass
{
public TestValue TestMethod
{
get
{
// BREAKPOINT WILL BE HIT WHEN DEBUGGING BUT THE EXCEPTION DISAPPEARS
throw new Exception("WATCH ME DISAPPEAR");
return new TestValue { Lbl = "Label", Txt = "Text" };
}
}
public class TestValue
{
public string Lbl { get; set; }
public string Txt { get; set; }
}
}
}
Exceptions thrown by data bindings get translated into trace, which are delivered to the DataBindingSource TraceSource, which has a source name of "System.Windows.Data".
You can convert these into exceptions by creating a subclass of TraceListner that throws an exception on a trace, and add it to the Listeners collection of the TraceSourceSource. This can be done either in code or in your App.config file.
Here is how you would do it in code:
System.Diagnostics.PresentationTraceSources.DataBindingSource.Listeners.Add(
new MyTraceListener());
See TraceSource and TraceListener documentation and samples for more details.
Exceptions get eaten by data bindings, and result in the data binding being disabled (I'm not 100% sure this is the case here, I'm too lazy to put it into VS).
Another possibility is that you may be hitting http://support.microsoft.com/kb/976038 if you are running a 64-bit OS.

Resources