My class looks like this:
public class testclass
{
public List<otherClass> references { get { return _references; } }
}
My otherClass looks like this
public class otherClass
{
public string name { get; set; }
}
And now i try to access this "otherClass" inside a DataTemplate
<DataTemplate x:Key="templateCore" DataType="{x:Type vm:AdminInterfaceViewModel}" >
<GroupBox DataContext="{Binding references }">
...
</DataTemplate>
this works fine, or i think at least, beaucse intellisense autocomplete it. But now how can i get access to the name property of the "otherClass" ?
All you need is to binding the List to a ItemsControl type,such as ListBox,DataGrid etc,and the ItemsControl will use the 'otherClass' instance in the List as the DataContext for each item in it.So you can find a 'mapping' there:
'List<otherClass>'--'ItemsControl'
'otherClass'--'Item'
.
I suppose that 'AdminInterfaceViewModel' is your DataContext,and 'references' is one property of it, so try this:
<DataTemplate x:Key="templateCore" DataType="{x:Type vm:AdminInterfaceViewModel}" >
<GroupBox>
<ListBox ItemsSource="{Binding references}">
<ListBox.ItemTemplate>
<DataTemplate>
<TexBox Text="{Binding name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
</DataTemplate>
> Update:
1.Suppose that you have a MainViewModel which contains a property named MyViewModel in type of 'AdminInterfaceViewModel '.
class MainViewModel
{
public AdminInterfaceViewModel MyViewModel {get; set;}
}
2.You have set the 'MainViewModel' as the DataContext of your Window,then you can use the property 'MyViewModel' in xaml.
<Window>
<Grid>
<ContentControl Margin="20" Content="{Binding MyViewModel}">
</ContentControl>
</Grid>
</Window>
3.Define the DataTemplate in your ResourceDictionary such as 'generic.xaml'.Remove the x:Key then the DataTemplate will automatically applied to every 'AdminInterfaceViewModel' type instance.
<DataTemplate x:Key="templateCore" DataType="{x:Type vm:AdminInterfaceViewModel}" >
<GroupBox>
<ListBox ItemsSource="{Binding references}">
<ListBox.ItemTemplate>
<DataTemplate>
<TexBox Text="{Binding name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
</DataTemplate>
> Tips:
Check this link,it may solve your potential problems:MVVM pattern
Related
I am using d:DesignInstance to get design-time data in the designer. My model looks like this (simplified):
interface IModel {
public ObservableCollection<IConversation> OpenConversations { get; }
}
enum ConversationType {
Channel, IM
}
interface IConversation {
public string Name { get; }
public ConversationType ConversationType { get; }
}
Then I have a mock-model with a couple of entries in the OpenConversations property. This works great when used as the ItemsSource in my ListView. My simplified XAML view looks like this:
<Page d:DataContext="{d:DesignInstance Type=mocks:MockModel, IsDesignTimeCreatable=True}">
<ListView ItemsSource="{Binding OpenConversations}"/>
</Page>
The above example works as expected and I get design time data.
However now I would like to add grouping in my ListView using CollectionViewSource so I added the following to my XAML:
<Page.Resources>
<CollectionViewSource x:Name="OpenConversations" IsSourceGrouped="True" />
</Page.Resources>
And changed the ListView to:
<ListView ItemSource="{StaticResource OpenConversations}" />
What I cannot really figure out though is how to get the design data into the
CollectionViewSource, I tried the following but it doesn't work:
<CollectionViewSource ... Source="{Binding OpenConversations}" />
According to the documentation at https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh780627.aspx I need to (in the code-behind) assign CollectionViewSource.Source = from conversation in OpenConversations group by conversation.ConversationType into group select group. But I cannot figure out how to do that using design-time data.
First try to define how a group shoud look I mean title of group ,and many more
public class Group<T> : ObservableCollection<T>, INotifyPropertyChanged
{
public Group(string name,IEnumerable<T> items):base(items)
{ Name=name;
}
//more ...
}
This class is for more than static data ,it handles changing data.
Now if you use MVVM model define in your viewmodel(home) a property ListGroup (name how you desire) of type new ObservableCollection>
PS not real code just mockup to give an ideea.
var s = from val in OpenConversations
group val by val.Conversationb into g
select new Group<IConversation>(g.Key.ToString(), g);
GroupList = new ObservableCollection<Group<IConversation>>(s);
Now create a just like you did before a CollectionViewSource in resouce
<CollectionViewSource x:Key="cvs" IsSourceGrouped="True" Source="{Binding Source={StaticResource home},Path=GroupList}"/>
<ListView ItemsSource="{Binding Source={StaticResource cvs}}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Background="Salmon" Width="100" Height="100">
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle >
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="LightGray" >
<TextBlock Text='{Binding Name}' Foreground="Black" Margin="10"
Style="{StaticResource SubheaderTextBlockStyle}" />
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
See how a define GroupSyle you add more properties in here
It work best for complete control i'm using it in w10 app.
Plus a has a partial design time helps(Shows only the list) :)) Not showing the header of group(GroupView)
Inside my class MainWindow I have:
public ObservableCollection<ViewModel> VMs ..
The MainWindow is constructed in the XAML (it creates an empty VMs in the class constructor too):
<Window.Resources>
<c:MainViewModel x:Key="ViewModelsSource"/>
</Window.Resources>
When I click on a button, I add ViewModel objects to the ObservableCollection VMs and the content of the ObservableCollection is shown in a ListBox:
<StackPanel DataContext="{Binding Source={StaticResource ViewModelsSource}}">
<ListBox IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding VMs}"
Background="Transparent"
HorizontalContentAlignment="Stretch"
> ...
The code for the Command Add is:
void AddListExecute()
{
VMs.Add(new ViewModel());
}
The constructor for ViewModel is:
public class ViewModel : MainViewModel
{
//Private Members
private ObservableCollection<FeeViewModel> _fees;
//Properties
public ObservableCollection<FeeViewModel> FVMs
{
get
{
return _fees;
}
set
{
_fees = value;
}
}
//Constructor
public ViewModel()
{
this._fees = new ObservableCollection<FeeViewModel>();
}
...
This part is working fine. Each ViewModel object contains another ObservableCollection:
public ObservableCollection<FeeViewModel> FVMs ..
I have a tabcontrol in the XAML that uses this ObservableCollection to do stuff:
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding FVMs, diag:PresentationTraceSources.TraceLevel=High}"
Style="{StaticResource EnabledTabs}" Grid.Column="1" Margin="0,0,10,0">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
...
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
...
EnabledTabs is a style that uses a property in FeeViewModel:
<Style TargetType="{x:Type TabControl}" x:Key="EnabledTabs">
<Setter Property="IsEnabled" Value="{Binding GotFees}"/>
</Style>
Now I have a binding error, FVMs is null and nothing is shown in the window. If I revert to a previous version without an ObservableCollection of ViewModel objects and I set the DataContext of the TabControl to that single ViewModel everything works fine.
How to set the DataContext of the TabControl to the dynamically created ViewModel objects?
Is it possible to do something like VMs/FVMs in the binding?
Thanks
Solved by adding DataContext to TabControl:
<TabControl
DataContext="{Binding VMs, Source={StaticResource ViewModelsSource}}"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding FVMs, diag:PresentationTraceSources.TraceLevel=High}"
Style="{StaticResource EnabledTabs}" Grid.Column="1" Margin="0,0,10,0">
I have a ListBox, its data source is an observable array of some objects. These objects all are derived from a base class.
I am trying to give each instance of the array a certain data template to handle its differences with other instances.
abstract class Base
{
public string a {get; set;};
}
class sub1 : Base
{
public string prop1 {get; set;};
}
class sub2 : Base
{
public string prop2 {get; set;};
}
If the array contains two instances, one is sub1, the other sub2, the list box should display for the first the two properties a and prop1, and for the second instance a and prop2.
Please advise,
You can create a DataTemplateSelector:
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate Sub1Template { get; set; }
public DataTemplate Sub2Template { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is sub1) { return Sub1Template; }
if (item is sub2) { return Sub2Template; }
return null;
}
}
Then use it as follows:
<UserControl>
<UserControl.Resources>
<DataTemplate x:Key="TemplateForSub1">
<StackPanel>
<TextBlock Text="{Binding a}" />
<TextBlock Text="{Binding prop1}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="TemplateForSub2">
<StackPanel>
<TextBlock Text="{Binding a}" />
<TextBlock Text="{Binding prop2}" />
</StackPanel>
</DataTemplate>
<my:MyTemplateSelector x:Key="MySelector"
Sub1Template="{StaticResource TemplateForSub1}"
Sub2Template="{StaticResource TemplateForSub2}" />
</UserControl.Resources>
<ListBox ItemsSource="{Binding SomeCollectionSomewhere}"
ItemTemplateSelector="{StaticResource MySelector}" />
</UserControl>
That should get you started.
Update: You can certainly use <DataTemplate DataType="{x:Type ...}" ...> to select a data template strictly based on the item type. It may be simpler to do so in a number of cases. A DataTemplateSelector can offer some flexibility that DataType= cannot, such as changing a template based on a value inside a class, or the results of a method call, etc. Choose whichever one works for you.
You need to specify a template for each of the types you want to display. Try something like this:
<ListBox ItemsSource="{Binding MyArray}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:sub1}">
<StackPanel>
<TextBlock Text="{Binding a}"/>
<TextBlock Text="{Binding prop1}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:sub2}">
<StackPanel>
<TextBlock Text="{Binding a}"/>
<TextBlock Text="{Binding prop2}"/>
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
MyArray is the array containing your instances.
local is the namespace for your classes sub1 and sub2
Say I have the following class, Employee
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public ObservableCollection<Employee> Underlings { get; set; }
}
And then I have the following XAML, bound to an ObservableCollection<Employee> MyEmployees
<ListBox ItemsSource="{Binding Path=MyEmployees}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Tag="{Binding Path=Employee.Id}">
<TextBlock Text="{Binding Path=Employee.Name}"></TextBlock>
<!-- Here's where I declare my underlings -->
<ListBox ItemsSource="{Binding Path=Employee.Underlings}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Tag="{Binding Path=Employee.Id}">
<TextBlock Text="{Binding Path=Employee.Name}"></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This allows each employee in the collection MyEmployees to have some underlings. But those underlings are also of type employee, and could have their own underlings. How do I cater for those additional levels without making my XAML very complex?
Is there some way to declare my DataTemplate separately and allow it to be referenced within itself?
Do I have to do all this from code-behind instead?
(I realise the XAML above may not be 100% correct, its just an example)
therefore you have to use a TreeView and not a ListBox. And You have to specify a HierarchicalDataTemplate.
You could define the DataTemplate inside the ListBoxes Resources as the default template for Employees:
<ListBox ItemsSource="{Binding MyEmployees}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type myns:Employee}">
<Grid Tag="{Binding Id}">
<TextBlock Text="{Binding Name}"></TextBlock>
<ListBox ItemsSource="{Binding Underlings}" />
</Grid>
</DataTemplate>
</ListBox.Resources>
</ListBox>
I have some XAML code in Window.Resources:
<ContextMenu x:Key="ParentContextMenu">
<MenuItem Header="MenuItem..." Command="{Binding SomeCommand}"/>
</ContextMenu>
<DataTemplate x:Key="ChildDataTemplate" DataType="{x:Type System:String}">
<TextBlock Text="{Binding}" VerticalAlignment="Bottom" />
</DataTemplate>
<HierarchicalDataTemplate x:Key="ParentDataTemplate" DataType="{x:Type ViewModels:IParentViewModel}" ItemsSource="{Binding Path=Agents}" ItemTemplate="{StaticResource ChildDataTemplate}">
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource ParentContextMenu}">
<TextBlock Text="{Binding ServerName}" />
</StackPanel>
</HierarchicalDataTemplate>
and then
<TreeView ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ParentDataTemplate}"/>
Why is SomeCommand not bound to the Context menu item?
I am sure that DataContext contains a ViewModel because other commands are working well. Any ideas please?
Any problem with this XAML.
So, I think you need to ckeck your class that implements IParentViewModel.
1) To my mind, your SomeCommand like SomeCommand : ICommand. Verify that access modifier is public(public class SomeCommand : ICommand)
2) Verify that access modifier of command property is public
(e.g.
public SomeCommand SomeCommand
{
get { return _someCommand; }
set
{
_someCommand = value;
OnPropertyChanged("SomeCommand");
}
}
3) Verify that you have created command instance (private SomeCommand _someCommand = new SomeCommand();)
Also,do that in case if you have dependency property instead.
Hope it helps.