Binding Data Template element to property on sub-class - c#

I have a class, for experiment sake call it foo() and another class, call it bar()
I have a data template for class foo() defined in my xaml, but one of foo()'s properties is a bar() object such that
foo()
{
Public string Name {get; set;}
Public int ID {get; set;}
Public bar barProp {get; set;}
}
and
bar()
{
Public string Description{get; set;}
}
I want my data template of foo to display the Description property of bar.
I have tried the simple <textblock Text="{Binding Path=barProp.Description}" /> and variants to no avail
Seeking wisdom,
DJ
EDIT:
In accordance with requests for more information...
here are my real classes...
public class AccountRecord
{
public string Value { get; set; }
public string Identifier { get; set; }
public Field AccountNumber;
}
public class Field
{
public string Name{get;set;}
public string Value{get;set}
}
and here is the XAML used to template them them...
<ListBox Margin="0,35,0,0" Name="listAccountRecords" Background="Transparent" BorderBrush="Transparent" ItemsSource="{Binding AccountRecords, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type lib:AccountRecord}">
<Expander Header="{Binding AccountNumber.Name}">
<ListView ItemsSource="{Binding Fields}" ItemTemplate="{DynamicResource FieldTemplate}">
</ListView>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And the exact problem is that the AccountNumber.Name value is not showing up in the expander header for the AccountRecord element

Your "AccountNumber" member (of type "Field") is only a field, not a property. You can only bind to properties. Give it a getter and setter and it'll start working.

Try This
<textblock Text="{Binding Path=FooObjectName.barProp.Description}" />
Hope this will work..
Good Luck !

Related

WPF TabControl: a dictionary for tab headers

I've got a TabControl that is bound to its viewmodel Items property:
<TabControl ItemsSource="{Binding Items}"/>
This is the viewmodel class:
public class ViewModel
{
public ObservableCollection<object> Items { get; } = new ObservableCollection<object>();
public ViewModel()
{
Items.Add(new Person() { FirstName = "Alan", LastName = "Turing" });
Items.Add(new Car() { ModelName = "Fiesta", Manifacturer = "Ford" });
}
}
Items's type is ObservableCollection<object>; this is because each tab represents a different kind of object, in this example a Person and a Car.
Question
I want to bind the tab's header to a text that is not available as a property of the bound classes, let say a manually provided header.
For example, I would like the first tab to have the text "Person" as header and the second to have the text "Car" as header:
I thought of:
adding the Header property to each class. It's easy and straightforward from the binding point of view, but this is sort of a generalized viewer and not all the interfaces of the input data can be modified.
an "attached property" would still require to act at class-level; yet feasible but probably a bit overkill
A Dictionary<object, string> defined at the viewmodel level, let's call it Headers.
Each time i add an object to the collection, i would also add an Header to the Headers dictionary. Its key would be the object itself.
Person p = new Person() { FirstName = "Alan", LastName = "Turing" };
Headers.Add(p, "Person");
Items.Add(p);
But how to write the binding expression then?
<TabControl ItemsSource="{Binding Items}">
<TabControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding ????????}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
Even if possible, this solution seems to be not very 'binding-friendly'.
There seems to be two problems here: dynamically attach an information to an instance, and access it via binding.
Any ideas?
Postscript - the code
The code, for completeness.
My classes:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Car
{
public string ModelName { get; set; }
public string Manifacturer { get; set; }
}
My DataTemplate, defined in the <Winodow.Resources> tag.
<DataTemplate DataType="{x:Type local:Person}">
<StackPanel>
<Label Content="{Binding FirstName}"/>
<Label Content="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Car}">
<StackPanel>
<Label Content="{Binding Manifacturer}"/>
<Label Content="{Binding ModelName}"/>
</StackPanel>
</DataTemplate>
You can also override the ToString() method for your model classed and return your friendly name from there.
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return "Person";
}
}
Template
<TabControl ItemsSource="{Binding Items}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>

x:Bind works but Binding doesn't (opposite to most Q&A found)

Most of the Q&A I found on StackOverflow is how Binding work but x:Bind doesn't which usually solved by Bindings.Update(). However, my issue is, inside a GridView, ItemSource="{x:Bind _myList}" works but ItemSource="{Binding _myList}" doesn't.
Why? And how do I make Binding work? (instead of x:Bind)
Here's a few code thingies:
Class:
public class MyClass
{
public string prop1 {get; set;}
public string prop2 {get; set;}
}
public class MyList : List<MyClass>
{
public void Populate()
// Add items
}
Code Behind
public MyList _myList = new MyList();
_myList.Populate();
DataContext = this;
Bindings.Update();
XAML (doesn't work here but works if ItemSource: changed into x:Bind _myList)
<GridView ItemSource="{Binding _myList}">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding prop1}"/> <TextBlock Text="{Binding prop2}/>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The problem that your _myList is field, not property. So, change to
public MyList _myList { get; set; } = new MyList();

Design Mode Error "Object does not match target type" for Loaded Page Resource

I am currently attempting to mock up data for my view by utilizing Design Data for my ViewModel. Specifically, I have a View front end, and a ViewModel backend for my silverlight application.
When I have mocked up other views, things have worked perfectly. Even in this particular view, the only issue seems to have to be with collections.
Any idea why my "CategoryItem" keeps giving me an error when I try to assign a value to "CategoryName"? I have no idea what is causing the issue...
Code below:
My Design Data:
<vm:MainPageViewModel
xmlns:vm="clr-namespace:WebCatalog.ViewModels"
xmlns:m="clr-namespace:WebCatalog.Models"
SelectedTab="Category 1"
ProjectName="New Project Name"
ShowPopup="False"
IsBusy="False"
CurrentUser="Alex"
>
<vm:MainPageViewModel.Categories>
<m:CategoryItem CategoryName="test"/>
</vm:MainPageViewModel.Categories>
</vm:MainPageViewModel>
My simplified ViewModel:
public class MainPageViewModel {
public string SelectedTab {get;set;}
public string ProjectName {get;set;}
public bool ShowPopup {get;set;}
public bool IsBusy {get;set;}
public string CurrentUser {get;set;}
public ObservableCollection<CategoryItem> Categories {get;private set;}
public MainPageViewModel()
{
Categories = new ObservableCollection<CategoryItem>();
}
}
Finally, my (simplified) view:
<UserControl
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d:DataContext="{d:DesignData Source=../SampleData/MainWindowSampleData.xaml}">
<!-- Decision Categories -->
<StackPanel Width="200" toolkit:DockPanel.Dock="Left" Height="100">
<TextBlock Text="{Binding CurrentUser}">meep</TextBlock>
<ItemsControl Height="100" ItemsSource="{Binding Categories}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>Itemalkdjfa;ldfj;lakdsjfladfjal;dfjaldfja</TextBlock>
<TextBlock Text="{Binding CategoryName}"></TextBlock>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
On your VM change
public ObservableCollection<CategoryItem> Categories {get;private set;}
to
public ObservableCollection<CategoryItem> Categories {get;set;}
Otherwise my sample xaml binding looked and worked in design mode like this:
<local:MainVM x:Key="myMainVM">
<local:MainVM.Categories>
<local:CategoryItem Name="Test" />
</local:MainVM.Categories>
</local:MainVM>
...
<ListBox DataContext="{StaticResource myMainVM}"
ItemsSource="{Binding Categories}">
<ItemsControl.ItemTemplate>
<DataTemplate><TextBlock Text="{Binding Name}"/></DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
Where the VM mirrored yours (sans the private set) and the VM was loaded in Xaml on Silverlight and showed the data in design mode.
public class MainVM
{
public ObservableCollection<CategoryItem> Categories { get; set; }
public MainVM()
{
Categories = new ObservableCollection<CategoryItem>();
}
}
public class CategoryItem
{
public string Name { get; set; }
}

WPF Combobox with ItemsSource - What path do I set to access this databinding?

I'm having a problem setting the Binding/Path property in my XAML.
I know this ComboBox's ItemSource property is updating properly, since I get a bunch of empty text-boxes when I update the viewmodel (instead of textboxes with text, which is what I expect).
So I believe the Binding in the DataTemplate section of my ComboBox needs a different binding path, but I'm not sure what to set the binding path to.
<ComboBox ItemsSource="{Binding Path=Locations}" Visibility="{Binding SettingsOptionsVisibility}" Grid.Column="0" x:Name="locationCB" VerticalAlignment="Top" SelectionChanged="locationCB_SelectionChanged" HorizontalAlignment="Left" Width="350" Height="30" IsHitTestVisible="False" IsEnabled="False" Focusable="False">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Here's a picture of my LocationCB's ItemsSource in the Watch List in my Codebehind: http://imgur.com/x4SYWER
As expected, my combobox is populated with 8 (textless) elements. What do I need to do to get my binding the text to Name to connect up?
EDIT: code for the Locations object in the ViewModel:
public ObservableCollection<Location> Locations { get; set; }
And code for the Location class wrapped in the observable collection, as requested:
public class Location
{
public Guid LocationID;
public Guid ParentID;
public String Name;
public bool isValid;
}
Change the fields of your location object to properties:
public class Location
{
public Guid LocationID { get; set; }
public Guid ParentID { get; set; }
public String Name { get; set; }
public bool isValid { get; set; }
}
You should be binding to properties and not variables. Instead of
public string Name;
You should change the above to
public string Name {get;set;}
Even better, can implement the INotifyPropertyChanged..

Manage combobox and complex objects

I am trying to figure out how to handle Combobox with complex objects.
I have the following 2 classes:
BackupVersion.cs
public class BackupVersion
{
public string Name { get; set; }
public BackupVersion() { }
public BackupVersion(string name)
{
Name = name;
}
}
SubsystemVersions.cs
public class SubsystemVersions : ObservableCollection<BackupVersion>
{
public SubsystemVersions()
{
Add(new BackupVersion("amit"));
Add(new BackupVersion("aaa"));
Add(new BackupVersion("ofir"));
}
}
I also have to following XAML window:
<Grid>
<StackPanel>
<StackPanel.Resources>
<local:SubsystemVersions x:Key="Backups"/>
</StackPanel.Resources>
<ComboBox Name="c1"
ItemsSource="{StaticResource Backups}"
Text="mmm"
DisplayMemberPath="Name"
SelectedValuePath="Name"
IsEditable="true"
IsReadOnly="true"/>
<TextBlock Text="{Binding ElementName=comboBox1, Path=SelectedItem}"/>
</StackPanel>
</Grid>
This way, in the code behind, I can get the selected string in the combobox using:
this.c1.SelectedValue.ToString()
My question is, how can I get back the original object i.e. the BackupVersion object?
Please also comment on the coding style, if I am doing something which is not common (for example, is that the best way to define and bind the collection?)
To get back the original object :
this.c1.SelectedItem;

Categories