Using Interaction.Triggers in DataTemplate causing XamlParseException only in Designer - wpf

I have an ItemsSource with a bound List of objects:
<ItemsControl ItemsSource="{Binding Sprites}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle Fill="Black"
Width="{Binding Width}"
Height="{Binding Height}"/>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<cmd:EventToCommand Command="{Binding MouseDownOnSpriteCommand}"
PassEventArgsToCommand="True"
EventArgsConverter="{StaticResource EventToEventCommandConverter}"
EventArgsConverterParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UIElement}, AncestorLevel=1}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now as you can see i tried adding a DataTemplate for the bound Items, however this causes the Visual Studio Designer to throw me the following error message:
XamlParseException: Collection property 'System.Windows.Controls.Grid'.'Triggers' is null.
StackTrace:
at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlReader templateReader, XamlObjectWriter currentWriter)
at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlObjectWriter objectWriter)
at System.Windows.FrameworkTemplate.LoadOptimizedTemplateContent(DependencyObject container, IComponentConnector componentConnector, IStyleConnector styleConnector, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)
at System.Windows.FrameworkTemplate.LoadContent(DependencyObject container, List`1 affectedChildren)
at System.Windows.StyleHelper.ApplyTemplateContent(UncommonField`1 dataField, DependencyObject container, FrameworkElementFactory templateRoot, Int32 lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate)
at System.Windows.FrameworkTemplate.ApplyTemplateContent(UncommonField`1 templateDataField, FrameworkElement container)
at System.Windows.FrameworkElement.ApplyTemplate()
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Canvas.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.UIElement.UpdateLayout()
My Code however compiles perfectly fine and I can run and execute it without any Problems. The issue is that, as long as the DataTemplate Code is uncommented, the Designer only shows me the Error Message.
I know that the DataTemplate is causing the Problem, because when i hardcode the same Grid into the ItemsControl and then use Interactions.Triggers, I can bind the Event and no Error appears in the Designer. (However I can only bind to Commands in the MainViewModel and not to the actual Items List).
So far I have tried to move the Template into a seperate ResourceDictionary, yet this hasn't changed anything.
Also I know that someone had the same problem before. However Im using Visual Studio 2019 and I have already updated to the newest Version and again the Error still appears.

So I haven't found a direct solution but a workaround.
One thing to note is that I defined my Window's DataContext by using
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
And I believe that somehow influenced the error.
And after testing, I have found that if I move the assigment of my DataContext to code behind, the designer doesn't show the error anymore and works fine.
So basically I used
public MainWindow() {
InitializeComponent();
MainViewModel vm = new MainViewModel();
DataContext = vm;
}
as constructor for my code behind.

Related

Focusing Textbox in a DataGrid on Checked Event of a CheckBox

I have two tasks that need to be done when a check box is checked. This check box is defined in a DataGrid (which is bound to a collection) as a data template of DataGridTemplateColumn.
Also, there is a DataGridTemplateColumn with data template of text box.
On checked event of a check box, I have to clear the text of the text box on the same row and also set the focus on that text box.
Clearing the text works fine on using the EventTrigger.
Problem is the second part where I have to set the focus on the text box.
TO achieve this goal, I thought to use attached behavior with ChangePropertyAction.
For this, I created a dependency property, hoping to get the reference of the datagrid and finding the text box on which focus needs to be set (which I'll do later).
public class TestDependencyProperty : DependencyObject
{
public static readonly DependencyProperty
FlagCheckedProperty =
DependencyProperty.RegisterAttached(
"FlagChecked", typeof(bool), typeof(TestDependencyProperty),
new PropertyMetadata(false, PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var datagrid = dependencyObject as DataGrid;
//get the textbox from the datagrid on which focus
//has to set and set the focus
}
public static bool GetFlagChecked(
DependencyObject d)
{
return (bool)d.GetValue(FlagCheckedProperty);
}
public static void SetFlagChecked(
DependencyObject d, bool value)
{
d.SetValue(FlagCheckedProperty, value);
}
}
and here is the xaml
<DataGrid ItemsSource="{Binding FlagCollection}" AutoGenerateColumns="False" CanUserAddRows="False" Margin="5"
VerticalScrollBarVisibility="Auto" v:TestDependencyProperty.FlagChecked="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Non Kittable" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Flag,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Window}},Path=DataContext.FlaggedCheckedCommand}"
CommandParameter="{Binding}"/>
<isc:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type DataGrid}}}" PropertyName="FlagChecked" Value="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Tracking No." Width="2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding XTrackingNum,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
But, on checking the check box I get error saying, "Cannot find a property named "v:TestDependencyProperty.FlagChecked" on type "DataGrid"
I also tried binding in this way
, but it didn't work -
"Cannot find a property named "FlagChecked" on type "DataGrid".
Not sure if this is the right way to do this.
Can someone point me in right direction..?? Thanks in advance..
In the xaml.cs you have set the focus of the textbox.
Create an event on checkbox when it is checked.
<CheckBox Name="sampleChkbox" Grid.Row="0" Grid.Column="0" Content="Flag" Checked="sampleChkbox_Checked"/>
<TextBox Name="txtSample" Grid.Row="2" Width="200px" Height="40"/>
xaml.cs
private void sampleChkbox_Checked(object sender, RoutedEventArgs e)
{
CheckBox chk = sender as CheckBox;
if((bool)chk.IsChecked)
txtSample.Focus();
}
Finally got it working by adding a boolean property "FocusOnChecked" to the class whose collection is getting binded to the datagrid and setting property as true or false on checked and unchecked event of checkbox using ChangePropertyAction.
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Window}},Path=DataContext.FlaggedCheckedCommand}"
CommandParameter="{Binding}"/>
<isc:ChangePropertyAction TargetObject="{Binding}" PropertyName="FocusOnChecked" Value="True"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<isc:ChangePropertyAction TargetObject="{Binding}" PropertyName="FocusOnChecked" Value="False"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Then created a attached property for textbox and binded it with the new property.
public class FocusBehavior : DependencyObject
{
public static readonly DependencyProperty
FocusProperty =
DependencyProperty.RegisterAttached(
"Focus",
typeof(bool),
typeof(FocusBehavior),
new FrameworkPropertyMetadata(false, PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var txtBox = dependencyObject as TextBox;
if (txtBox == null)
return;
if (dependencyPropertyChangedEventArgs.NewValue is bool == false)
return;
if ((bool )dependencyPropertyChangedEventArgs.NewValue)
txtBox.Focus();
}
public static bool GetFocus(
DependencyObject d)
{
return (bool)d.GetValue(FocusProperty);
}
public static void SetFocus(
DependencyObject d, bool value)
{
d.SetValue(FocusProperty, value);
}
}
below is the xaml for textbox.
<DataTemplate>
<TextBox vm:FocusBehavior.Focus="{Binding Path= FocusOnChecked}" Text="{Binding TrackingNum,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
</TextBox>
</DataTemplate>

Binding fails when used inside MenuItem.Icon

I want to offer a context menu with an item that has a color swatch in the space where "icons" are normally placed for such menu items, i.e. the space corresponding to MenuItem.Icon.
But the color swatch is dynamic--a Brush property on the UserControl that (in this crafted example) changes to a random color in response to the ContextMenuOpening event--and my attempt at binding to it is failing.
When run, the menu item has no content in the Icon space, and Visual Studio's output contains an error that doesn't seem like it ought to be happening.
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ContextMenu', AncestorLevel='1''. BindingExpression:Path=PlacementTarget.RandomBrush; DataItem=null; target element is 'Rectangle' (Name=''); target property is 'Fill' (type 'Brush')
Here's the XAML for the control:
<UserControl x:Class="ContextMenuItemIconTest.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"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Do something">
<MenuItem.Icon>
<Rectangle Width="16" Height="16" Fill="{Binding PlacementTarget.RandomBrush, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</UserControl.ContextMenu>
<Grid>
</Grid>
And the code behind:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace ContextMenuItemIconTest
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
ContextMenuOpening += UserControl1_ContextMenuOpening;
}
void UserControl1_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
Random r = new Random();
RandomBrush = new SolidColorBrush(Color.FromRgb((byte)r.Next(256), (byte)r.Next(256), (byte)r.Next(256)));
}
#region RandomBrush (Dependency Property)
public Brush RandomBrush
{
get { return (Brush)GetValue(RandomBrushProperty); }
set { SetValue(RandomBrushProperty, value); }
}
public static readonly DependencyProperty RandomBrushProperty =
DependencyProperty.Register(
"RandomBrush",
typeof(Brush),
typeof(UserControl1),
new PropertyMetadata(new SolidColorBrush(Colors.Blue)));
#endregion
}
}
Not sure if there is a better solution but I think the scenario here is very tricky. The Icon content seems to be detached completely from the visual tree. So you cannot use Binding with RelativeSource or ElementName and strangely even setting the Source to some {x:Reference} causes some cyclic reference error.
I just could think of this work-around, a little hacky but it's acceptable. There is an interesting knowledge about the Freezable object. Binding inside it (set for some property) can use RelativeSource as well as ElementName even when it's just declared as a resource (added to Resources). So in this case we can try using some object deriving from Freezable to act as the proxy. Because this proxy is declared as a resource, we can set the Binding inside Icon with Source being set to some StaticResource referencing the proxy. Then it would work. There are many objects deriving from Freezable for your choice, you can even create your own class. But I would like to use something existing here. The DiscreteObjectKeyFrame is the most suitable object to use. Technically its Value property can hold any kind of object. Now's the working code:
<ContextMenu>
<ContextMenu.Resources>
<!-- the proxy here -->
<DiscreteObjectKeyFrame x:Key="o" KeyTime="0"
Value="{Binding PlacementTarget.RandomBrush,
RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu.Resources>
<MenuItem Header="Do something">
<MenuItem.Icon>
<Rectangle Width="16" Height="16"
Fill="{Binding Value, Source={StaticResource o}}" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>

WPF ItemsControl.ItemsTemplate Code Behind

I am currently using an ItemsControl template that binds to the the ViewModel to present a collection of objects. I have a ToggleButton as part of the template. I would like to access the object that is bound to that UI item in the collection in the code behind.
Here is the code that I have in place:
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal">
<ToggleButton Cursor="Hand"
IsChecked="{Binding IsActive, Mode=TwoWay}"
IsEnabled="{Binding CanToggleOnProfile}"
Style="{StaticResource ProfileToggleButtonStyle}"
PreviewMouseLeftButtonUp="OnProfileToggle">
I would like to in my code behind on the OnProfileToggle call, access that particular object in the DataTemplate and do some stuff with it, but I can't seem to figure out how to access it (what index it is at in the collection, etc).
You will find your particular object in the DataContext of the sender:
private void OnProfileToggle(object sender, MouseButtonEventArgs e)
{
ToggleButton button = sender as ToggleButton;
object yourItem = button.DataContext;
}
Of course you have to cast yourItem to your item class.

ToolBar item DataTemplate binding RelativeSource search failing to find parent ToolBar

I have a ToolBar containing Buttons, some of the Buttons have only an Image for content, others have only Text. I am trying to bind the width property of the Button Image to a custom Property on my derived ToolBar class. It works sometimes but fails other times with the following error:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='NuiWpfCore.Controls.ToolBar', AncestorLevel='1''. BindingExpression:Path=IconSize; DataItem=null; target element is 'Image' (Name=''); target property is 'Width' (type 'Double')
Here is the xaml containing the element binding that is failing. The DataTemplate is returned from a DataTemplateSelector which is created inline:
<pres:ToolBar x:Class="NuiWpfCore.Controls.ToolBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:pres="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:core="clr-namespace:NuiWpfCore"
xmlns:ctrls="clr-namespace:NuiWpfCore.Controls"
xmlns:select="clr-namespace:NuiWpfCore.Selectors"
xmlns:converters="clr-namespace:NuiWpfCore.Converters"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase">
<ToolBar.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/NuiWpfCore;component/Controls/MenuBarTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
<converters:ListPairToStringConverter x:Key="ListPairToStringConverter" />
<converters:IconMetaDataToImageConverter x:Key="IconMetaDataToImageConverter" />
<converters:IconMetaDataToImageConverterParameter x:Key="IconToImageConverterParameter"
ConvertToImage="False" Width="16" Height="16" />
</ResourceDictionary>
</ToolBar.Resources>
<ToolBar.ItemTemplateSelector>
<select:ToolBarItemDataTemplateSelector>
<!-- other DataTemplates omitted for brevity -->
<select:ToolBarItemDataTemplateSelector.IconCommand>
<DataTemplate DataType="{x:Type core:PropertyElement}">
<Button IsEnabled="{Binding Path=CanEdit}" Command="{Binding}">
<Button.Content>
<Image
Width="{Binding Path=IconSize,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ctrls:ToolBar}} }"
Height="{Binding Path=Width,
RelativeSource={RelativeSource Self}}"
Source="{Binding Path=MetaData,
Converter={StaticResource IconMetaDataToImageConverter},
ConverterParameter={StaticResource IconToImageConverterParameter}}"/>
</Button.Content>
</Button>
</DataTemplate>
</select:ToolBarItemDataTemplateSelector.IconCommand>
<!-- other DataTemplates omitted for brevity -->
</select:ToolBarItemDataTemplateSelector>
</ToolBar.ItemTemplateSelector>
</pres:ToolBar>
Here is the ToolBar class with the Source Property for the binding.
public partial class ToolBar : System.Windows.Controls.ToolBar, Views.IView
{
public ToolBar() : base()
{
InitializeComponent();
IconSize = 32;
}
public int IconSize { get; set; }
}
This ToolBar class is sometimes used in a ToolBarTray and other times it is not, but the bind search fails in both cases in certain scenarios.
Does anybody have any ideas as to why this might be failing?
Have you considered making IconSize on your ToolBar an inherited property?
public static readonly DependencyProperty IconSizeProperty =
DependencyProperty.RegisterAttached( "IconSize", typeof(double), typeof(ToolBar ),
new FrameworkPropertyMetadata(32, FrameworkPropertyMetadataOptions.Inherits));
public static double GetIconSize(DependencyObject target)
{
return (double)target.GetValue(IconSizeProperty);
}
public static void SetIconSize(DependencyObject target, double value)
{
target.SetValue(IconSizeProperty, value);
}
Then you can just access the IconSize like
<Button.Content>
<Image
Width="{Binding RelativeSource={RelativeSource Self}, ctrls::ToolBar.IconSize}"
Height="{Binding Path=Width,RelativeSource={RelativeSource Self}}"
Source="{Binding Path=MetaData,
Converter={StaticResource IconMetaDataToImageConverter},
ConverterParameter={StaticResource IconToImageConverterParameter}}"/>
First you should set it on your toolbar, and every other element down the tree can access this property.
Sorry out of my head, not 100% guarenteed to be correct. But the overall idea of
Value Inheritance is a good way to solve this.
The DataTemplate looks like it is being defined inside a DataTemplateSelector declaration, which isn't part of the Visual Tree, and so won't be able to navigate up from there if the Binding were being evaluated in that spot. Where is the template actually being applied?

How to get the PlacementTarget on WPF Context menu when item click using MVVM pattern

How to get the PlacementTarget of selected ContextMenu when using MVVM pattern ?
I can get PlacementTarget like this when using MenuItem click event. But how it do it MVVM ?
public void changeGaugeColor(object sender, RoutedEventArgs e)
{
MenuItem tempMenuItem = (MenuItem)sender;
MenuItem mi = (MenuItem)sender;
bd = (BidirectionalDial)cm.PlacementTarget;
}
I realise that this is an old post, but someone might find this useful to know. Assuming that you have bound your view to your view model, then you can then bind your ContextMenu.DataContext to your view model in the following way:
First, name your view UserControl... I generally name all of mine This for simplicity. Then remembering that our view model is bound to the DataContext of the UserControl, we can bind to the view model using {Binding DataContext, ElementName=This}.
So now we can bind to the view model, we have to connect that with the ContextMenu.DataContext. I use the Tag property of the object with the ContextMenu (the PlacementTarget) as that connection, in this example, a Grid:
<DataTemplate x:Key="YourTemplate" DataType="{x:Type DataTypes:YourDataType}">
<Grid ContextMenu="{StaticResource Menu}" Tag="{Binding DataContext,
ElementName=This}">
...
</Grid>
</DataTemplate>
We can then access the view model properties and commands in the ContextMenu by binding the ContextMenu.DataContext property to the PlacementTarget.Tag property (of the Grid in our example):
<ContextMenu x:Key="Menu" DataContext="{Binding PlacementTarget.Tag, RelativeSource=
{RelativeSource Self}}">
<MenuItem Header="Delete" Command="{Binding DeleteFile}" CommandParameter=
"{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource
AncestorType=ContextMenu}}" CommandTarget="{Binding PlacementTarget,
RelativeSource={RelativeSource Self}}" />
</ContextMenu>
Note the binding on the MenuItem.CommandTarget property. Setting this ensures that the target element on which the specified command is raised is the PlacementTarget, or the Grid in this case.
Also note the CommandParameter binding. This binds to the DataContext of the PlacementTarget, or the Grid in this case. The DataContext of the Grid will be inherited from the DataTemplate and so your data item is now bound to the object parameter in your Command if you're using some implementation of the ICommand interface:
public bool CanExecuteDeleteFileCommand(object parameter)
{
return ((YourDataType)parameter).IsInvalid;
}
public void ExecuteDeleteFileCommand(object parameter)
{
Delete((YourDataType)parameter);
}
Or if you are using some kind of RelayCommand delegates directly in your view model:
public ICommand Remove
{
get
{
return new ActionCommand(execute => Delete((YourDataType)execute),
canExecute => return ((YourDataType)canExecute).IsInvalid);
}
}

Resources