Binding to property of first item in related (detail) collection - c#

Similar to WPF: How to bind to only one item in a collection, not using ItemsControl since I don't want to display all of them, except the collection is related to the main binding item. All the data come in via EntityFramework. As with the linked question, the xaml explains it best:
<StackPanel Grid.Row="1" Orientation="Horizontal" DataContext="{Binding CurrentCustomer}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Total Orders:" />
<TextBlock Text="{Binding Orders.Count}" />
</StackPanel >
<StackPanel Orientation="Horizontal">
<TextBlock Text="First Order:" />
<TextBlock Text="{Binding Orders.First.OrderDate}" />
</StackPanel >
</StackPanel>
As you can see, I guessed that since I could get a count of the related orders using 'Orders.Count', I tried to utilise the same linq syntax to try to retrieve Orders.First (I also tried Orders.FirstOrDefault), but this doesn't work.
Looking at the linked question, I tried Sheridan's '[]' syntax, but Orders[0].OrderDate give an output error of: System.Windows.Data Error: 40 : BindingExpression path error: '[]' property not found on 'object' ''HashSet'1' (HashCode=37425772)'. BindingExpression:Path=Orders[0].OrderDate; DataItem='Customer_<BigLongIdentityString>' (HashCode=21972018); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String').
Looking at the msdn article he linked to, I tried wrapping the expression in brackets (as per attached properties) and using slashes (as for when the source is a collection). Not unexpectedly, these failed too.
Can someone show me how I get to the property of the first item in a related collection?

If possible, you can always add a new property to your model that exposes the first item:
public Order FirstItem
{
get { return this.Orders.First(); }
}
Then, you can just bind to that new property.
Also, this answer is highly relevant. A hashset may not be a good fit, here. There's no indexer. I generally stick to ObservableCollections.
Also, with regard to EF generation compatible with databinding, take a look at this MSDN article. Read the section called
Updating code generation for data binding
You can mess with the T4 template and per the article,
Find and replace the first occurrence of “HashSet” with
“ObservableCollection”.
Happy WPF'ing.

Related

How do you add a button to a TreeView using a HierarchicalDataTemplate?

I've got a TreeView that is constructed like this:
//This is for dynamically building a treeview with templates from an XML file
XmlTextReader xmlReader1 = new XmlTextReader("HierarchicalDataTemplate1.xml");
HierarchicalDataTemplate hierarchicalDataTemplate1 = XamlReader.Load(xmlReader1) as HierarchicalDataTemplate;
And it reads an XML file like this:
<HierarchicalDataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ItemsSource="{Binding XPath=SubCategory}">
<TextBlock FontSize="36" FontFamily="K22 Monastic" Text="{Binding XPath=#CategoryName}" />
<Button>Add Subordinate Unit</Button>
</HierarchicalDataTemplate>
But it throws a runtime error on adding the button:
''Template' property has already been set on 'HierarchicalDataTemplate'.' Line number '3' and line position '4'.
Is what I'm trying to do possible? If I take out the script for adding a button everything works fine.
Thanks!
One obvious error is that you've got two elements at the root level of the template's visual tree. You can't do that. A DataTemplate or HierarchicalDataTemplate can have only one child. So your first step is to make that one child a control that supports multiple children of its own, then put your TextBlock and your Button in that. StackPanel is a good one:
<HierarchicalDataTemplate
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
ItemsSource="{Binding XPath=SubCategory}"
>
<StackPanel Orientation="Horizontal">
<TextBlock
FontSize="36"
FontFamily="K22 Monastic"
Text="{Binding XPath=#CategoryName}"
/>
<Button>Add Subordinate Unit</Button>
</StackPanel>
</HierarchicalDataTemplate>
It's interesting to note that when I paste your template XAML into the XAML designer, I get a different error: "The property 'VisualTree' is set more than once" -- but when I duplicate your XamlReader.Load(), code, I get the same exception and message as you (and the same fix corrects it).
Google turns up zero results for "Template property has already been set on HierarchicalDataTemplate". Well, maybe it'll have one now.

WPF get all bindings pointing to a property in my model

I have a scenario like this
<StackPanel DataContext="{Binding WorldConfig}">
<TextBox Text="{Binding Width}">
</StackPanel>
I want to get all bindings pointing to "Width" property of WorldConfig object
I wrote a function for this as
BindingUtil.GetAllBindings(WorldConfig, "Width")
but it searches through all windows to find a FrameworkElement whose DataContext equals to the first parameter and then searches for a BindingExpression whose ResolvedSourcePropertyName equals to the second parameter. And it is quite inefficient. I thought of caching the results but then any change to bindings in runtime will break it.
I'm looking for a more efficient way to do this.
I'm thinking that these BindingExpressions must be stored in somewhere I can query but I can't find it, only search results I'm getting for my searches are about how to do the opposite of this.

Binding to a COM Object - Cannot resolve a Property

I am currently running into some trouble with binding objects to a WPF ListBox. The number of Elements is populated correctly but the Name property cannot be accessed. I get the following error message:
System.Windows.Data Error: 40 : BindingExpression path error: 'Name' property not found on 'object' ''__ComObject' (HashCode=17252513)'.
BindingExpression:Path=Name; DataItem='__ComObject'
(HashCode=17252513); target element is 'TextBlock' (Name=''); target
property is 'Text' (type 'String')
However, I am able to print this property correctly to the console at runtime.
It looks like the object is not casted correctly
XAML:
<ListBox Name="lbSelectConfiguration" Margin="10" ItemsSource="{Binding Configurations}">
<ListBox.ItemTemplate>
<StackPanel Margin="5" Orientation="Horizontal">
<Image Source="/Resources/Configuration.png" Margin="0,0,8,0" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind
this.DataContext = viewModel;
this.viewModel = viewModel;
foreach (Configuration config in this.viewModel.Configurations)
{
Console.WriteLine(config.Name);
}
Can you help me out? Thank you.
Because __ComObject is mentioned in the error, I am presuming that the collection of objects you are binding to, are actually COM objects that implement the "Configuration" interface, rather than CLR ones.
(whether that COM object is actually implemented using C# (with ComVisible(true)) or C++ is immaterial, it's the fact you are using an "interface" - am presuming it is coming from an "COM interop library" i.e. after you did an add reference to a COM library?).
You can try this:
<TextBlock Text="{Binding (mynamespacewithinterface:Configuration.Name)}" />
Otherwise you can look at these links to understand the difficulty in the Binding system resolving bindings (COM interfaces and NET interfaces each have their own quirks/limitations), and some workarounds:
https://social.msdn.microsoft.com/Forums/en-US/69389bb5-ba90-4a22-8a75-e1a21f1d3a16/comobject-in-datatemplate?forum=wpf
http://badecho.com/2012/07/adding-interface-support-to-datatemplates/
https://social.msdn.microsoft.com/Forums/vstudio/en-US/1e774a24-0deb-4acd-a719-32abd847041d/data-templates-and-interfaces
WPF databinding to interface and not actual object - casting possible?
https://social.msdn.microsoft.com/Forums/vstudio/en-US/1e774a24-0deb-4acd-a719-32abd847041d/data-templates-and-interfaces?forum=wpf
http://blog.pmunin.com/2012/01/xaml-datatemplates-binding-to-interface.html
http://leecampbell.blogspot.co.uk/2008/09/generic-binding-in-wpf-through-explicit.html
http://www.dev102.com/2008/04/23/wpf-datatemplate-for-interfaces-not-supported/
As far as I can remember, ComObjects are dynamic in .NET, so the compiler will never recognise the names of the object's properties at compile time. If that's really important for you, then just create a .NET wrapper object that implements the INotifyPropertyChanged interface and copy all of the object's properties to your WPF object. The compiler will then obviously be able to read the names of the properties.

Items collection must be empty before using ItemsSource. Telerik Treeview

Im getting this error while trying to giva my treeview an itemsource
"Items collection must be empty before using ItemsSource."
I have checked a lot of solutions and I cant seem to find a way to solve this. Here are my code snippets:
XAML:
<HierarchicalDataTemplate x:Key="Category">
<TextBlock Text="{Binding Name}">
</TextBlock>
</HierarchicalDataTemplate>
XAML
<telerik:RadTreeView x:Name="treeview" IsDragDropEnabled="True" HorizontalAlignment="Left" Height="250" Margin="10,10,0,-3" VerticalAlignment="Top" Width="190" IsManipulationEnabled="True" IsLoadOnDemandEnabled="True" LoadOnDemand="treeview_LoadOnDemand" IsExpandOnSingleClickEnabled="True" ItemTemplate="{StaticResource Category}">
</telerik:RadTreeView>
C# - Giving the treeview a data source:
Data d = new Data();
treeview.ItemsSource = d.Get_Categories();
C# - My database query:
public List<Category> Get_Categories()
{
using (var context = new ProcessDatabaseEntities())
{
return context.Category.ToList();
}
}
Category only has two properties, Name and ID. I know that the itemsource-list is not empty when I assign it. So it's probably something wrong with my XAML-code. Thank you in advance!
I believe that your problem is a common one. Basically, you cannot use both the TreeView.ItemsSource and the TreeView.Items properties together... you must choose one way or the other. Usually this problem manifests itself because a developer has done something like this...:
<TreeView Name="TreeView" ItemsSource="{Binding SomeCollection}" ... />
... and then tried to do something like this in the code behind:
TreeView.Items.Add(someItem);
The solution in that case would be to manipulate the data bound collection instead of the TreeView.Items collection:
SomeCollection.Add(someItem);
However, in your case (and it's a little bit difficult to guess without seeing your code), you have probably done the second part first (set or manipulated the Items property) and then tried to set the ItemsSource property. Your solution is the same... use one method of editing the items or the other... not both.

providing source for an Image control

I need to databind into a element inside my custom class. i've given the ItemSource as the ObservableCollection of telerik:RadTransitionControl via an attached property. However, I need to provide the image member as the source to Image control. I tried the following method and was unsuccessful.
<Grid Background="Black">
<telerik:RadTransitionControl x:Name="radControl" adRotator:AdRotatorExtensions.ItemChangeDelay="0:0:3"
adRotator:AdRotatorExtensions.CurrentSelectedIndex="0"
adRotator:AdRotatorExtensions.IndexChanged="{Binding TopItemCommand, Mode=OneWay}"
adRotator:AdRotatorExtensions.ItemsSource="{Binding Path=ImagePaths}"
VerticalAlignment="Center"
HorizontalAlignment="Center" Width="650">
<telerik:RadTransitionControl.Transition>
<telerik:MotionBlurredZoomTransition />
</telerik:RadTransitionControl.Transition>
<telerik:RadTransitionControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding Path=ImagePaths.AdImage}" />
</DataTemplate>
</telerik:RadTransitionControl.ContentTemplate>
</telerik:RadTransitionControl>
</Grid>
an ImagePaths object is already set as the DataContext for the item. this means that the binding is already pointing (so to speak) at an instance of the object. so, when you tell it to bind on ImagePaths.AdImage it does not know how to find the property you are looking for. Good news is, all you have to do is provide the path on the object-- remove the ImagePaths part (and the dot) and you should be good to go.
for example...
class something
{
public string someImage {...}
}
<DataTemplate> <!--------- the data context of this item is an instance of
my "something" class, so i need to set the path
to be the property on the object --->
<Image Source="{Binding Path=someImage}" />
</DataTemplate>
here is a very helpful article on debugging bindings in WPF
for more info here is an excellent article on MSDN
here is a datatemplate article from Dr. WPF

Categories

Resources