I'm seeing the following binding expression being thrown from a TreeView control ONLY when I've clicked on any of the root items in the tree view - if I click on any other item I DO NOT see a binding expression until I click on a root Item then I see the binding exception all the time.
The binding expression path is missing leading, 'Asset' & 'Trade' are values of a Name property on the Node class, which is a hierarchical data structure, it has a child collection of type Node etc...
The items are bound to the TreeView using a ListCollectionView.
.Net 4.0 WPF app running on Win7.
The class bound to the TreeView is as follows:
internal class Node : INotifyPropertyChanged
{
public string Id { get; private set; }
public string Name { get; private set; }
public Node Parent { get; private set; }
public IEnumerable<Node> Children { get; private set; }
}
Any ideas why this is happening?
System.Windows.Data Error: 40 : BindingExpression path error: 'Asset'
property not found on 'object' ''Node' (HashCode=-729436854)'.
BindingExpression:Path=Asset; DataItem='Node' (HashCode=-729436854);
target element is 'TreeView' (Name='FieldTreeView'); target property
is 'NoTarget' (type 'Object')
System.Windows.Data Error: 40 :
BindingExpression path error: 'Trade' property not found on 'object'
''Node' (HashCode=-729436854)'. BindingExpression:Path=Trade;
DataItem='Node' (HashCode=-729436854); target element is 'TreeView'
(Name='FieldTreeView'); target property is 'NoTarget' (type 'Object')
This is the XAML for the TreeView, it being used as part of a WPF user control:
<TreeView Name="FieldTreeView"
Grid.Row="1"
Margin="0,15,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
SelectedItemChanged="FieldTreeViewOnSelectedItemChanged">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children, Mode=OneWay}">
<Grid>
<TextBlock Text="{Binding Path=Name, Mode=OneWay}" />
<Grid.ContextMenu>
<ContextMenu ItemsSource="{Binding Path=FieldMenu, Mode=OneWay}"
DisplayMemberPath="Title"
Visibility="{Binding Path=HasFieldMenu, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding}" />
<Setter Property="CommandParameter"
Value="{Binding Path=DataContext.Id, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Grid.ContextMenu>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded, Mode=TwoWay}" />
<EventSetter Event="MouseDoubleClick" Handler="FilterTreeViewOnItemMouseDoubleClick" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
The code used to populate the Node classes is shown below:
private void InitialiseFields()
{
_rootNode.RemoveAllChildren();
foreach (var field in _fields.OrderBy(x => x.Id))
{
NodeHelper.TokenizeIntoNodes(field, field.Id.Split(Node.IdSeperator), _menuMediator, _rootNode);
}
_nodes = _rootNode.Children.Cast<Node>().ToArray();
FieldTreeView.ItemsSource = _rootNode.Children;
}
Related
This question already has an answer here:
WPF ListView with group of RadioButtons and select default value
(1 answer)
Closed 4 months ago.
Have two properties in my viewmodel, INotifyPropertyChanged is implemented but not shown here. Both Properties are initiated in the constructor and values are set.
public ObservableCollection<Foo> Foos { get; init;}
public Foo SelectedFoo { get => _selectedFoo; set => _selectedFoo = value;}
the foo class:
public class Foo
{
public string NameOfFoo { get; set; }
public bool IsActive { get; set; }
}
in the corresponding View...
<ListView
ItemsSource="{Binding Foos}"
SelectedItem="{Binding SelectedFoo, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Background="#FF063852"
BorderThickness="0">
<ListView.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding NameOfFoo}"
GroupName="GroupSelector"
BorderThickness="0"
Style="{StaticResource RadioButtonStyle}"
IsChecked="{Binding IsActive}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding Path=DataContext.SelectGroupCommand,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</RadioButton>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Margin="5" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
ItemsSource of ListView is working fine.
The radioButton is not checked as expected after creating new instance from viewmodel.
The command for the event "checked" is ok.
The SETTER from the SelectedFoo propery is never called. Why??
What have I to do, to have the propper radiobutton checked in my view after creation?
Found a solution here: https://stackoverflow.com/a/24179089/14800168
Multibinding for IsChecked was the trick!
Following on from a previous question here, I'm struggling to style a MenuItem's Icon with a control I have that inserts icon images based upon a string dependency property.
Initially I started with:
<ContextMenu ItemsSource="{Binding MenuItems}">
<ContextMenu.Resources>
<Style TargetType="MenuItem">
...
<Setter Property="Icon">
<local:StringToIcon IconName="{Binding IconName}" />
</Setter>
</Style>
</ContextMenu.Resources>
</ContextMenu>
This had the predictable effect of only displaying one of the icons in the menu, usually the last one, as the instance was shared around.
I then tried the non-shared resource approach:
<ContextMenu ItemsSource="{Binding MenuItems}">
<ContextMenu.Resources>
<local:StringToIcon x:Key="MenuIcon" x:Shared="False" IconName="{Binding IconName}" />
<Style TargetType="MenuItem">
...
<Setter Property="Icon" Value="{StaticResource MenuIcon} />
</Style>
</ContextMenu.Resources>
</ContextMenu>
This had no effect. It didn't offer me x:Shared in Intellisense, so I wonder if that's an invalid property here.
Out of desperation, I threw the thing into a template:
<Setter Property="Icon">
<Setter.Value>
<ContentControl>
<ContentControl.Template>
<ControlTemplate>
<local:StringToIcon IconName="{Binding IconName}" />
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</Setter.Value>
</Setter>
Again, no effect. My StringToIcon looks like this at the moment, hard-coded with a single image to check the problem doesn't lie there. (Or does it?)
<UserControl x:Class="RAP.Admin3.Components.StringToIcon"
...
>
<Image DataContext="{Binding ElementName=StringIconControl}" Source="pack://application:,,,/Resources/Icons/lorry.png"/>
</UserControl>
How do I get this darn thing to template and allow multiple uses? It's probably something basic I'm overlooking.
I've looked at various similar questions, and most seem to have success with the non-shared resource method.
Edit: Let me add substantially more code as requested. I've come up with a minimal replication of the problem:
The context menu is part of a TreeView resource.
<UserControl x:Class="MyApp.ItemHierarchy"
...
Name="ItemHierarchyControl">
<Grid>
<TreeView ItemsSource="{Binding ElementName=ItemHierarchyControl, Path=Items}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:HierarchyItem}" ItemsSource="{Binding Subitems}">
<StackPanel Orientation="Horizontal" Margin="0,1,4,1">
<TextBlock Text="My text" VerticalAlignment="Center" />
<StackPanel.ContextMenu>
<ContextMenu ItemsSource="{Binding MenuItems}">
<ContextMenu.Resources>
<local:StringToIcon x:Key="MenuIcon" x:Shared="False" IconName="{Binding IconName}" />
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Name}" />
<Setter Property="Icon" Value="{StaticResource MenuIcon}" />
</Style>
</ContextMenu.Resources>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</UserControl>
This is backed by a dependency property for the items.
public ObservableCollection<HierarchyItem> Items
{
get { return (ObservableCollection<HierarchyItem>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<HierarchyItem>), typeof(ItemHierarchy), new PropertyMetadata(new ObservableCollection<HierarchyItem>()));
StringToIcon is also backed by a string dependency property for the icon name, which is summarily ignored because of the hard-coded image at the moment.
HierarchyItems are simple for the example:
public ObservableCollection<HierarchyItem> Subitems { get; set; }
public ObservableCollection<BindableMenuItem> MenuItems { get; set; }
Just to get this proof working, I attached the ItemHierarchy to some properties of the main window:
public ObservableCollection<BindableMenuItem> MenuItems { get; set; }
public ObservableCollection<HierarchyItem> IHItems { get; set; }
public MainWindow()
{
MenuItems = new ObservableCollection<BindableMenuItem>();
MenuItems.Add(new BindableMenuItem("Item", null));
MenuItems.Add(new BindableMenuItem("Item", null));
MenuItems.Add(new BindableMenuItem("Item", null));
MenuItems.Add(new BindableMenuItem("Item", null));
IHItems = new ObservableCollection<HierarchyItem>();
IHItems.Add(new HierarchyItem() { MenuItems = this.MenuItems });
InitializeComponent();
}
Edit 2: Here's BindableMenuItem also:
public class BindableMenuItem
{
public BindableMenuItem(string name, ICommand command)
{
this.Name = name;
this.Command = command;
}
public string Name { get; set; }
public ICommand Command { get; set; }
public string IconName { get; set; }
public ObservableCollection<BindableMenuItem> Children { get; set; }
}
Try to move the StringToIcon to <TreeView.Resources>:
<TreeView ItemsSource="{Binding ElementName=ItemHierarchyControl, Path=Items}">
<TreeView.Resources>
<local:StringToIcon x:Key="MenuIcon" x:Shared="False" IconName="{Binding IconName}" />
<HierarchicalDataTemplate DataType="{x:Type local:HierarchyItem}" ItemsSource="{Binding Subitems}">
<StackPanel Orientation="Horizontal" Margin="0,1,4,1">
<TextBlock Text="My text" VerticalAlignment="Center" />
<StackPanel.ContextMenu>
<ContextMenu ItemsSource="{Binding MenuItems}">
<ContextMenu.Resources>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Name}" />
<Setter Property="Icon" Value="{StaticResource MenuIcon}" />
</Style>
</ContextMenu.Resources>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I've listVIew on my first page. As soon as user double clicks on one item from that listVIew, I'm supposed to show a GridView where they can add/update/delete item. I've my code working until double click. I also see correct binding on my GridView and also getting correct count of items from database but UI doesn't show anything. Each individual item has a binding error of 40. Please help!
This is the error when I snoop QuestionCode:
System.Windows.Data Error: 40 : BindingExpression path error: 'QuestionCode' property not found on 'object' ''Char' (HashCode=4390979)'. BindingExpression:Path=QuestionCode; DataItem='Char' (HashCode=4390979); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
This is my xaml:
<DockPanel>
<DataGrid DockPanel.Dock="Top" x:Name="gridQuestionList" ItemsSource="{Binding Source=CheckListQuestions}" IsReadOnly="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Question Code" MinWidth="70">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center" Padding="3,0,8,0" Text="{Binding QuestionCode, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox VerticalAlignment="Center" Text="{Binding QuestionCode, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" Foreground="Black" IsReadOnly="False"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
This is my code behind:
public partial class CheckListQuestionWindow : Window
{
private string _checklistCode = string.Empty;
private CheckListQuestionViewModel _viewModel;
public CheckListQuestionWindow()
{
InitializeComponent();
}
public CheckListQuestionWindow(string checkListCode) : this()
{
_checklistCode = checkListCode;
if (_viewModel == null)
_viewModel = new CheckListQuestionViewModel(_checklistCode);
DataContext = _viewModel;
}
}
This is my ViewModel:
ObservableCollection<ChecklistQuestion> CheckListQuestions { get; set; }
public CheckListQuestionViewModel(string code)
{
var list = ChecklistQuestionList.GetChecklistQuestionList(code);
CheckListQuestions = list;
//here i'm getting correct count of checklistquestions but there's no data
}
This is my Model:
public string QuestionCode
{
get { return GetProperty(QuestionCodeProperty); }
set { SetProperty(QuestionCodeProperty, value); }
}
Replace
ItemsSource="{Binding Source=CheckListQuestions}"
by
ItemsSource="{Binding Path=CheckListQuestions}"
or just
ItemsSource="{Binding CheckListQuestions}"
When using Source=CheckListQuestions, the ItemsSource property gets the string "CheckListQuestions" as value, which is an IEnumerable<char>. Hence the error 'QuestionCode' property not found on 'object' ''Char'
Also make sure that CheckListQuestions is a public property, i.e.
public ObservableCollection<ChecklistQuestion> CheckListQuestions { get; set; }
instead of
ObservableCollection<ChecklistQuestion> CheckListQuestions { get; set; }
I have a problem when I try to bind a int value to a DependencyProperty in a custom control from a style.
MyClassVM contains an int named Number. It shows perfectly in the Label, when I bind in the same way, but will not set on my custom control. If I change from "{Binding Number}" To "15" for example, everything works great also on the custom control.
<Style TargetType="{x:Type ItemsControl}" x:Key="TestKey">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ItemsPresenter />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Style.Resources>
<DataTemplate DataType="{x:Type local:MyClassVM}">
<StackPanel Orientation="Horizontal">
<StackPanel.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding DataContext.OpenProjectCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" CommandParameter="{Binding .}" />
</StackPanel.InputBindings>
<ctrl:MyCustomControl Margin="5" Width="50" ctrl:MyCustomControl.ValueProperty="{Binding Number}"/>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Number}" FontSize="14" Foreground="{DynamicResource CeriseBrush}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</Style.Resources>
</Style>
This is how the MyCustomControl-class looks.
public partial class MyCustomControl: CustomUserControl
{
public int Value { get { return _value; } set { _value = value; } }
public MyCustomControl()
{
InitializeComponent();
DataContext = this;
}
public string ValueProperty
{
get { return (string)GetValue(ValuePropertyProperty); }
set { SetValue(ValuePropertyProperty, value); }
}
public static readonly DependencyProperty ValuePropertyProperty =
DependencyProperty.Register("ValueProperty", typeof(int), typeof(MyCustomControl), new UIPropertyMetadata(ValuePropertyChangedHandler));
public static void ValuePropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
((MyCustomControl)sender).Value = (int)e.NewValue;
}
}
The error I get looks like this:
System.Windows.Data Error: 40 : BindingExpression path error: 'Number' property not found on 'object' ''MyCustomControl' (Name='')'. BindingExpression:Path=Number; DataItem='MyCustomControl' (Name=''); target element is 'MyCustomControl' (Name=''); target property is 'ValueProperty' (type 'Int32')
Not sure, but I think you have to provide a relative source in your binding like:
Label Content="{Binding Number, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
Replace Window with UserControl if you're in a Control
I would like to bind an element inside a control template for a custom control to a property defined in a child class defined inside the custom control class. What might be the syntax for doing that (see {Binding ???})?
Some code...
C# code:
public class CustmCntrl : Control
{
// blablabla
public class SubChildClass: INotifyPropertyChanged
{
public double X { get; private set; }
public string info { get; private set; }
}
}
XAML:
<Style TargetType="{x:Type local:CustmCntrl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustmCntrl}">
<Grid> ... </Grid>
<ItemsControl
ItemsSource="{Binding stuffToDisplay, RelativeSource={RelativeSource TemplatedParent}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate >
<Grid> ... </Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:X
ToolTip="{Binding info ???}">
<local:X.RenderTransform>
<TranslateTransform X="{Binding X ???}"/>
</local:X.RenderTransform>
</local:X>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I can't test this at the moment, but you should be able to bind by adding the name of the SubChildClass class. Try something like this:
<TextBlock Text="{Binding SubChildClass.PropertyName, RelativeSource={RelativeSource
AncestorType={x:Type YourPrefix:YourCustomControl}}}">
If that doesn't work, try this:
<TextBlock Text="{Binding (SubChildClass.PropertyName), RelativeSource={RelativeSource
AncestorType={x:Type YourPrefix:YourCustomControl}}}">
You can find out more from the Property Path Syntax page on MSDN.
UPDATE >>>
Yeah, after getting a chance to test this out in a project, it seems that you can't data bind directly with class properties like that. However, if you just wanted to declare the class there as a container for some basic data items in your control then that is fine. As long as you define some DependencyPropertys to hold them, you can use that class just fine:
private static DependencyProperty ItemsProperty = DependencyProperty.Register("Items",
typeof(ObservableCollection<SubChildClass>), typeof(CustomControl1));
public ObservableCollection<SubChildClass> Items
{
get { return (ObservableCollection<SubChildClass>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
In Generic.xaml:
<ListBox ItemsSource="{Binding Items,
RelativeSource={RelativeSource AncestorType={x:Type local:CustmCntrl}}}" />