xaml inside xaml. How to bind controls - c#

I have xaml inside an xaml. The inside xaml has some bindings which are giving some problems. To explain here is my code
Main xaml
<TabItem Header="Configuration" DataContext="{Binding ComponentsVM}">
<Grid>
<ListBox ItemsSource="{Binding SomeList}" DisplayMemberPath="Name" SelectedItem="{Binding SomeComponent}" SelectedIndex="0"/>
<ig:MyInsideXamlElement Content="{Binding MyUserControl}" DataContext="{Binding}" />
</Grid>
</TabItem>
The inside xaml is
<Grid>
<TextBox Text="{Binding MySearchPath}"/>
</Grid>
The MyUserControl property displays my binding of UserControl without problems. But the MySearchPath property does not get updated with entity framework class. I suspect the binding of my inner xaml(MySearchPath) does not get resolved because the whole tab item's datacontext is ComponentsVM. Is there any way to give a second datacontext to the inner xaml?

Related

How to access data from ViewModel in ItemsSource tag

I'm making TabControl that can change dynamically using ItemsSource tag.
I want to know the way to access ViewModel data in ItemsSource tag.
I searched through the Internet. but I couldn't find the answer.
CODE
public class ViewModel
{
// this will be used in ItemsSource
private ObservableCollection<ActiveButton> _allExecuteButtonInfos = new ObservableCollection<ActiveButton>();
public ObservableCollection<ActiveButton> AllExecuteButtonInfos
{
get { return _allExecuteButtonInfos; }
set {
_allExecuteButtonInfos = value;
OnPropertyChanged();
}
}
// I want to get this data in ItemsSource
private List<string> _boardNameList = new List<string>();
public string BoardNameList
{
get { return _boardNameList; }
set {
_boardNameList = value;
OnPropertyChanged();
}
}
}
XAML
<Grid>
<TabControl Background="#FF292929" ItemsSource="{Binding AllExecuteButtonInfos}">
<TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto">
<Grid VerticalAlignment="Stretch" Margin="0,0,0,0" >
<ComboBox Width="334" Margin="0,0,0,0" Grid.Column="1" Grid.Row="1" Height="22" VerticalAlignment="Top"
<!-- I want to get data from ViewModel not in ItemsSource(AllExecuteButtonInfos) -->
<!-- eg) VM:BoardNameList, ViewModel.BoardNameList etc -->
ItemsSource="{Binding BoardNameList, Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedBoard, Mode=TwoWay}"/>
</Grid>
</ScrollViewer>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
I hope I can find the answer.
Thank you.
You could bind to the DataContext, i.e. the view model, of the parent TabControl using a RelativeSource:
<ComboBox ...
ItemsSource="{Binding DataContext.BoardNameList, RelativeSource={RelativeSource AncestorType=TabControl}}" />
Note that it's pointless to set the Mode of an ItemsSource binding to TwoWay since the control never sets the property. It's also meaningless to set the UpdateSourceTrigger to PropertyChanged in this case for the same reason.
I am not sure where you've defined the data context but I suppose that it's somewhere above the first 'Grid' markup. Something like this?
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
Then you have to somehow refer to the Datacontext of the window. You can do it this way
<ComboBox
ItemsSource="{Binding DataContext.BoardNameList, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" />
if the name of your view is not 'MainWindow', you have to change it to the view name where you have that code.
One of the best ways is to create a UserControl for each model and then put data templates in TabControl.Resources with DataType specified for all types you could put in ItemsSource - you get full customization of the view with nice seperation of XAML files.
<Grid>
<TabControl Background="#FF292929" ItemsSource="{Binding AllExecuteButtonInfos}">
<TabControl.Resources>
<DataTemplate DataType="{x:Type MyViewModel1}">
<MyViewModel1_View ViewModel="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type MyViewModel2}">
<MyViewModel2_View ViewModel="{Binding}"/>
</DataTemplate>
</TabControl.Resources>
</TabControl>
</Grid>
I'm going from memory, so the binding may be done differently, but that's the basic idea.
That, or you use some kind of ViewResolver as the only item in the TabControl (something like this)
Basically, go even more MVVM :)
Provided that the DataContext of your view is set correctly to your ViewModel and AllExecuteButtonInfos is indeed available in your view, you can use a RelativeBinding to access properties which are not in the DataContext of your current scope.
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.BoardNameList}" />
With that, you are leaving the implicit DataContext of the DataTemplate, which is ActiveButton and access the object of the specified type via AncestorType. From there you can set a Path to the DataContext of the UserControl, which is, in your case, an object of the class ViewModel.
Imaging you are climbing up a ladder. From the ComboBox object up to your UserControl, from where you can access all underlying properties.

WPF Binding directly to the DataContext

Is there a keyword to directly bind to the DataContext and not to an attribute of it?
I heard about the workaround with a Self Object. My problem is that I open a Window, and give an ObservableCollection as argument, which is set to the DataContext.
Here the WPF(xaml.cs) ctor
public Depot(ObservableCollection<ObservableCollection<ItemManager.Item>> totalDepot)
{
this.FullDepotList = totalDepot;
this.DataContext = FullDepotList[1];
InitializeComponent();
}
The XAML Code snippet where I would preferably bind to the DataContext directly or to "this":
<WrapPanel>
<ListBox
ItemsSource="{Binding this, UpdateSourceTrigger=PropertyChanged}"
ItemTemplate="{DynamicResource ItemWithCoolTooltipTemplate}"
Focusable="False">
</ListBox>
</WrapPanel>
To bind directly to the DataContext and not to an attribute of it, don't write any binding Path. Make it just {Binding}. UpdateSourceTrigger=PropertyChanged is not needed because ItemsSource doesn't change from view.
<ListBox
ItemsSource="{Binding}"
ItemTemplate="{DynamicResource ItemWithCoolTooltipTemplate}"
Focusable="False">
</ListBox>
alternatively use Path=. to code "bind entire DataContext here" requirement
<ListBox
ItemsSource="{Binding Path=.}"
ItemTemplate="{DynamicResource ItemWithCoolTooltipTemplate}"
Focusable="False">
</ListBox>
any tricks with RelativeSource/ElementName are usually necessary to change binding source. In this case DataContext (binding source) is simply inherited from parent Window.
You can try the following trick.
Add name property to your Window - <Window ... Name="myWindow" ...>
Use such a construction to bind to the property or whatever you need - <ListBox ItemsSource="{Binding Path=DataContext, ElementName=myWindow}" ... />

Reuse UserControl in TabControl with Various ViewModels

I have one user control that I would like to reuse with multiple ViewModels, all which implement the same interface. I would like to have these embedded in a TabControl.
Currently I can do this for a single instance but I am struggling to reuse my UserControl. For the single instance I can either bind the ViewModel in the UserContol's xaml or instantiate it in the code behind, however I can't figure out how to set this from a higher level.
Here is what I have,
<TabControl HorizontalAlignment="Left" Height="800" Margin="0,0,0,0" VerticalAlignment="Top" Width="600">
<TabItem Header="Tab1">
<Frame Source="SomeUserControl.xaml" BorderThickness="0" Margin="0" />
</TabItem>
</TabControl>
Here is pseudo code for what I would like to achieve,
<TabControl>
<TabItem Header="Tab1">
<Frame Source="{SomeUserControl.xaml, DataContext=ViewModel1}" />
</TabItem>
<TabItem Header="Tab2">
<Frame Source="{SomeUserControl.xaml, DataContext=ViewModel2}" />
</TabItem>
</TabControl>
Thanks!
Instead of hard coding the tabs, bind to an ObservableCollection of an "item" class. I.e. something like
class MyTabItems : INotifyPropertyChanged
{
public string Header...
public object DataContext...
}
create an:
ObservableCollection<MyTabItems>
Bind the TabControl ItemsSource to the ObservableCollection. In the item template for the TabControl bind the Header to the Header property and the DataContext to the DataContext property. The source can be hardcoded in the template or you can add another property to your item class and bind it to that if you want more flexibility.
Thanks to SledgeHammer's answer I was able to take a different approach and get things working.
<TabControl Name="TabControl" ItemsSource="{Binding TabItems}" HorizontalAlignment="Left" Height="800" Margin="0,0,0,0" VerticalAlignment="Top" Width="600">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<UserControl Content="{Binding DataContext}"/>
</DataTemplate>
</TabControl.ContentTemplate>
TabItems was populated in the code behind associated with the xaml above.
this.TabItems = new ObservableCollection<TabItem>
{
new TabItem("Main", new MainControl()),
new TabItem("Tab1", new GenericTabControl(new ViewModel1())),
new TabItem("Tab2", new GenericTabControl(new ViewModel2()))
};

How can I bind a datatemplate to a observablecollection so that they all show up?

How can I bind a datatemplate to reproduce itself for each item within an observablecollection so that they all show up? Or must I add these as individual controls, at runtime?
So I have a datatemplate:
<UserControl.Resources>
<DataTemplate x:Key="QueueItem">
<StackPanel Orientation="Vertical">
<Label Content="{Binding caseNumber}"></Label>
<!--TODO: put my other controls for this template here...-->
</StackPanel>
</DataTemplate>
</UserControl.Resources>
And I implement it in xaml here:
<ContentControl
Content="{Binding TestItems}"
ContentTemplate="{StaticResource QueueItem}">
</ContentControl>
And in my viewmodel there is a instance TestItems of some class I made, and one member of that is caseNumber, so the code above works just fine for me... But now I would like to have an observable collection of that same class, so is it possible/ is there xaml syntax to bind to the collection instance to reproduce the above for each index?
Thanks!
You could use an ItemsControl and use the ItemsTemplate property for your template:
<ContentControl>
<ItemsControl
ItemsSource="{Binding YourTestItemList}"
ItemTemplate="{StaticResource QueueItem}"/>
</ContentControl>
Use ItemsControl instead of ContentControl
Click here for sample code

Can't Reach ViewModel From Within ListBox

I have a ViewModel set as the highest level DataContext for my wpf application but I can't seem to access it when I jump into a ListBox as the new DataContext is the element of the list. A simple example is below.
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<StackPanel>
<!--1D List-->
<ListBox ItemsSource="{Binding my_list}">
<ListBox.ItemTemplate>
<DataTemplate>
<!--Individual Elements-->
<TetxBlock Text="{Binding ViewModel_DisplayString}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
That example won't actually work when it comes to the button click as the ViewModel_ClickFunction isn't on the items within the class.
So is there anyway for me to do {Binding parent.selected_item} or something like that? I just need to be able to access the ViewModel from within the ListBox.
The DataContext inside ItemTemplate is actually the item itself. So in this case you have to use RelativeSource to walk up the visual tree (to the ListBox) and change the Path to DataContext.ViewModel_DisplayString:
<TetxBlock Text="{Binding DataContext.ViewModel_DisplayString,
RelativeSource={RelativeSource AncestorType=ListBox}}"/>

Categories

Resources