Data bind an ItemsControl in a DataTemplate - c#

I seem to have a simple data-binding problem, but can't figure out the right way to do it. There is a TabControl which defines two DataTemplate's, one for the tab header and one for the tab content.
The content template contains an ItemsControl. The ItemsControl tries to bind to a dynamically created ViewModel (ConnectionInfoVM).
When I display the UI, the binding just fails, but there is no error-message in the output about it.
How do I have to set up the DataContext and the binding so the binding works and the DataBuffer is actually displayed? Any help greatly appreciated.
ConnectionsControl:
<UserControl x:Class="XXXViewer.Views.ConnectionsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:viewModels="clr-namespace:XXXViewer.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TabControl Grid.Row="0" Name="TabDynamic" SelectionChanged="tabDynamic_SelectionChanged">
<TabControl.Resources>
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
<Button Name="btnDelete" DockPanel.Dock="Right" Margin="5,0,0,0" Padding="0" Click="btnTabDelete_Click" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
<Image Source="{DynamicResource DeleteImg}" Height="11" Width="11"></Image>
</Button>
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="TabContent" DataType="viewModels:ConnectionInfoVM">
<StackPanel>
<ScrollViewer Name="Scroller" Background="Black">
<StackPanel>
<TextBlock Text="This line gets printed" Foreground="White" FontFamily="Consolas"/>
<ItemsControl Name="ItemCtrl" ItemsSource="{Binding DataBuffer}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}" Foreground="White" FontFamily="Consolas"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</StackPanel>
</DataTemplate>
</TabControl.Resources>
</TabControl>
</Grid>
</UserControl>
ConnectionsControl code behind:
namespace XXXViewer.Views
{
public partial class ConnectionsControl : UserControl
{
private readonly ObservableCollection<TabItem> _tabItems = new ObservableCollection<TabItem>();
public ConnectionsControl()
{
InitializeComponent();
// bindings
TabDynamic.ItemsSource = _tabItems;
TabDynamic.DataContext = this;
}
// assume this gets called
private void AddTabItem(ConnectionInfoVM ci)
{
DataTemplate headerTemplate = TabDynamic.FindResource("TabHeader") as DataTemplate;
DataTemplate contentTemplate = TabDynamic.FindResource("TabContent") as DataTemplate;
// create new tab item
TabItem tab = new TabItem
{
Header = $"Tab {ci.ConnectionID}",
Name = $"T{ci.ConnectionID}",
HeaderTemplate = headerTemplate,
ContentTemplate = contentTemplate,
DataContext = ci
};
_tabItems.Insert(0, tab);
// set the new tab as active tab
TabDynamic.SelectedItem = tab;
}
}
}
ConnectionInfoVM:
namespace XXXViewer.ViewModels
{
public class ConnectionInfoVM : ViewModelBase
{
private readonly ObservableQueue<string> _dataBuffer = new ObservableQueue<string>();
public ObservableQueue<string> DataBuffer => _dataBuffer;
}
}
Screenshot of the tab that gets created:
resulting tab

You set the ContentTemplate but never the Content, so the ContentTemplate is never applied because it's applied only when there's Content set. Instead of DataContext = ci write Content = ci.
By the way the DataContext = ci was useless because the DataContext is already implicitely the object on which the DataTemplate is applied.
Edit
As you're using WPF, use and abuse of its core feature: bindings.
How I would have written your code (if I didn't use full MVVM compliant code):
Your XAML:
<TabControl Grid.Row="0" Name="TabDynamic"
ItemsSource="{Binding TabItems, Mode=OneWay}"
SelectionChanged="tabDynamic_SelectionChanged">
<TabControl.Resources>
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
<Button Name="btnDelete" DockPanel.Dock="Right" Margin="5,0,0,0" Padding="0" Click="btnTabDelete_Click" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
<Image Source="{DynamicResource DeleteImg}" Height="11" Width="11"></Image>
</Button>
</DockPanel>
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate DataType="viewModels:ConnectionInfoVM">
<TabItem Header="{Binding ConnectionID, Mode=OneWay}"
Name="{Binding ConnectionID, Mode=OneWay}"
HeaderTemplate="{StaticResources TabHeader}">
<StackPanel>
<ScrollViewer Name="Scroller" Background="Black">
<StackPanel>
<TextBlock Text="This line gets printed" Foreground="White" FontFamily="Consolas"/>
<ItemsControl Name="ItemCtrl" ItemsSource="{Binding DataBuffer}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}" Foreground="White" FontFamily="Consolas"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</StackPanel>
</TabItem>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
You cs code become much simpler:
namespace XXXViewer.Views
{
public partial class ConnectionsControl : UserControl
{
private readonly ObservableCollection<ConnectionInfoVM> _tabItems = new ObservableCollection<ConnectionInfoVM>();
public ObservableCollection<ConnectionInfoVM> TabItems {get {return _tabItems;}}
public ConnectionsControl()
{
InitializeComponent();
// bindings
//TabDynamic.ItemsSource = _tabItems;
TabDynamic.DataContext = this;
}
// assume this gets called
private void AddTabItem(ConnectionInfoVM ci)
{
TabItems.Add(ci);
}
}
}
I noted while re-reading your code that you were probably confused about binding in code-behind.
Your code TabDynamic.ItemsSource = _tabItems; is not a binding, it will only set it once.
Anyway, I suggest you read a bit about MVVM. The TabItems should be in a ViewModel class instead of being in code-behind.

The Tabcontrol as per your coding does not contain in it's DataContext the viewmodel but the control; so we need to find a control or something else which holds the VM. It does not appear that the page holds the VM in its DataContext either.
I recommend that one route is to use the TabControl's Tag property to hold the VM such as specifying it in code behind as such:
TabDynamic.ItemsSource = _tabItems;
TabDynamic.DataContext = this;
TabDynamic.Tag = {Wherever you are keeping your VM at this time its not clear in your code example};
Then you can specify Tag from the template binding by specifying the TabControls' name as such:
<ItemsControl Name="ItemCtrl"
ItemsSource="{Binding Tag.DataBuffer,
ElementName=TabDynamic}">

Related

Rectangle wrapped in button does not trigger Command

For school i have to make a WPF ui in c# the domain code is already made by the teacher.
i have to handle click events on a rectangle, i chose to wrap the rectangle around a button.
here is a part of my xaml where i make the rectangle
<Window x:Class="View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ViewModel;assembly=ViewModel"
xmlns:controls="clr-namespace:View.Controls"
mc:Ignorable="d"
x:Name="Picross"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:PiCrossControl x:Name="picrossControl" Margin="0,0,10.2,-0.2" Grid="{Binding Grid}" RowConstraints="{Binding RowConstraints}" ColumnConstraints="{Binding ColumnConstraints}">
<controls:PiCrossControl.SquareTemplate x:Uid="rect">
<DataTemplate>
<Button Command="{Binding Path=DataContext.OnClick, ElementName=Picross}">
<Rectangle IsHitTestVisible="False" Width="40" Height="40" Stroke="Black" Grid.Column="0" StrokeThickness="2">
<Rectangle.Fill>
<Binding Path="Contents.Value">
<Binding.Converter>
<local:SquareConverter Empty="White" Filled="#123456" Unknown="Gray"/>
</Binding.Converter>
</Binding>
</Rectangle.Fill>
</Rectangle>
</Button>
</DataTemplate>
</controls:PiCrossControl.SquareTemplate>
<controls:PiCrossControl.RowConstraintsTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Values}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Width="40" Height="40" Text="{Binding Value}" TextAlignment="Center" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</controls:PiCrossControl.RowConstraintsTemplate>
<controls:PiCrossControl.ColumnConstraintsTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Values}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Width="40" Height="40" Text="{Binding Value}" TextAlignment="Center" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</controls:PiCrossControl.ColumnConstraintsTemplate>
</controls:PiCrossControl>
<Button Grid.Column="1" Width="150" Height="20" Command="{Binding CheckSolution}">
<TextBlock Text="check solution"/>
</Button>
</Grid>
the binding OnClick does not register for some reason.
here is my datacontext of the xaml
public class GameModel
{
private IPlayablePuzzle PlayablePuzzle;
public GameModel()
{
var puzzle = Puzzle.FromRowStrings("xxxxx", "x...x", "x...x", "x...x", "xxxxx");
var facade = new PiCrossFacade();
this.PlayablePuzzle = facade.CreatePlayablePuzzle(puzzle);
//Console.WriteLine(string.Join("\n", PlayablePuzzle.RowConstraints.Items.Select(x=>x.ToString())));
//var vmGrid = this.PlayablePuzzle.Grid.Map(square => new SquareViewModel(square)).Copy();
this.CheckSolution = new CheckSolution(PlayablePuzzle);
this.OnClick = new OnClick();
}
public IGrid<IPlayablePuzzleSquare> Grid => PlayablePuzzle.Grid;
public IEnumerable<IPlayablePuzzleConstraints> RowConstraints => this.PlayablePuzzle.RowConstraints;
public IEnumerable<IPlayablePuzzleConstraints> ColumnConstraints => this.PlayablePuzzle.ColumnConstraints;
public string Test => "Test";
#region Commands
public ICommand CheckSolution { get; }
public ICommand OnClick { get; }
#endregion
}
as you can see i have 2 ICommands checksolution is also bound to a button in my xaml and that button does work (it is not wrapped around a rectangle it is just a button wrapped around a textblock) the ICommand OnClick is the one bound to my rectangle button.
here is my OnClick code
public class OnClick : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
Console.WriteLine(parameter);
}
}
update:
i set the name of window to picross and updated my binding on the button and now get the following error:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=Picross'. BindingExpression:Path=DataContext.OnClick; DataItem=null; target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
This happens because the command is being bound from within a DataTemplate. You will need to "walk back" to the parent to find the data context.
Something like this:
Command="{Binding Path=DataContext.OnClick, ElementName=MainWindowName}"
In this example I am assuming you set the GameModel as the DataContext of a Window named "MainWindowName".
So you can add your name to your Window like this:
x:Name="MainWindowName"
The you can assign your data context in your window constructor like this:
public MainWindow()
{
InitializeComponent();
DataContext = new GameModel();
}
This binding can be expressed in other ways.
Another way this can be done is using Reference like this:
Command="{Binding Path=DataContext.OnClick, Source={x:Reference picrossControl}}"

WPF binding different UserControls in a DataTemplate of TabControl

As a new in WPF and MVVM light, I am struggling to apply the MVVM pattern in a TabControl. I will give you an example of what I am trying to achieve.
TabOne xaml and its view model
<UserControl x:Class="TestTabControl.TabOne"
xmlns:local="clr-namespace:TestTabControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="tab one ..." FontWeight="Bold" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</UserControl>
//TabOne ViewModel
class TabOne : ViewModelBase
{
public string TabName
{
get
{
return "TabOne";
}
}
}
TabTwo xaml and its viewmodel
<UserControl x:Class="TestTabControl.TabTwo"
xmlns:local="clr-namespace:TestTabControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="tab two ..." FontWeight="Bold" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</UserControl>
//TabTwo ViewModel
class TabTwo : ViewModelBase
{
public string TabName
{
get
{
return "TabTwo";
}
}
}
and finally the MainWindow xaml and its viewmodel
<Window x:Class="TestTabControl.MainWindow"
xmlns:local="clr-namespace:TestTabControl"
mc:Ignorable="d"
Title="Test Tab Control" MinWidth="500" Width="1000" Height="800">
<TabControl ItemsSource="{Binding TabViewModels}" >
<TabControl.ItemTemplate >
<!-- header template -->
<DataTemplate>
<TextBlock Text="{Binding TabName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
?????????
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Window>
//MainWindow ViewModel
class MainWindowViewModel : ViewModelBase
{
private ObservableCollection<ViewModelBase> _tabViewModels;
public MainWindowViewModel()
{
_tabViewModels = new ObservableCollection<ViewModelBase>();
TabViewModels.Add(new TabOne());
TabViewModels.Add(new TabTwo());
}
public ObservableCollection<ViewModelBase> TabViewModels
{
get
{
return _tabViewModels;
}
set // is that part right?
{
_tabViewModels = value;
RaisePropertyChanged(() => TabViewModels);
}
}
}
What am I supposed to write in the DataTemplate? Can I pass both usercontrols for TabOne and TabTwo in this DataTemplate in order to get the view for each tab I click? Or do I need to write another DataTemplate?
You may already knew the answer by now. But for the benefits of other people, what you need to do is:
<Grid Margin="10">
<Grid.Resources>
<DataTemplate DataType="{x:Type local:TabOne}">
<local:UserControlOne/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:TabTwo}">
<local:UserControlTwo/>
</DataTemplate>
</Grid.Resources>
<TabControl Margin="10"
ItemsSource="{Binding TabViewModels}">
</TabControl>
</Grid>
Please note that, your UserControl for TabOne ViewModel is also named TabOne.
I changed it to UserControlOne. Same applies to UserControlTwo.

WPF databinding not working for border or textblock in styled tabitem

I am trying to bind a border's background and text in a textblock to change on events. Both of these items exist inside a tab header which has been styled for the application.
If I hard code the color and text everything works properly, but when I try to bind the background color and text they no longer appear. What am I missing to get the databinding working properly?
xaml
<UserControl x:Class="Project.TabPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:Project"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="600">
<TabControl Style="{StaticResource LeftTabControl}" Background="#FAFAFAFA" HorizontalAlignment="Stretch">
<TabItem x:Name="ConnectionLabelTab" Style="{StaticResource Tab2}" Focusable="False">
<TabItem.HeaderTemplate>
<DataTemplate>
<Border x:Name="ConnectionLabelBorder" Background="{Binding LabelColor}" Width="70">
<TextBlock x:Name="ConnectionLabelText"
Text="{Binding LabelText}" Padding="0,4,0,4"
Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="10"/>
</Border>
</DataTemplate>
</TabItem.HeaderTemplate>
</TabItem>
</TabConrol>
</UserControl>
xaml.cs
/// <summary>
/// Interaction logic for TabPanel.xaml
/// </summary>
public partial class TabPanel : UserControl
{
String green = "#FF0A7E07";
BrushConverter bc = new BrushConverter();
Brush labelcolor;
String labeltext;
public TabPanel()
{
labelcolor = (Brush)bc.ConvertFromString(green);
labeltext = "Connected";
InitializeComponent();
}
public Brush LabelColor { get { return labelcolor; } }
public String LabelText { get { return labeltext; } }
}
You have the wrong DataContext in you DataTemplate, simply add:
DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext}"
to the BorderControl in your DataTemplate, complete Template:
<DataTemplate>
<Border x:Name="ConnectionLabelBorder" DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext}" Background="{Binding LabelColor}" Width="70">
<TextBlock x:Name="ConnectionLabelText"
Text="{Binding LabelText}" Padding="0,4,0,4"
Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="10" />
</Border>
</DataTemplate>

create a ListBox inside an UserControl and bind objects

I want to create a ListBox inside a UserContorl, and than, using that userControl to show and "manage" that list in many pages.
For example i got a list of trucks, each object truck has some property like the name, the id...
Now i create my own UserControl
<UserControl
x:Class="Crud.View.ListboxInUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Crud.View"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Name="myUserControl"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<ListBox x:Name="aName" ItemsSource="{Binding ??}">
<StackPanel>
<StackPanel Orientation="Vertical" Margin="0,20,0,0">
<TextBlock Text="Id"/>
<TextBlock Text="{Binding Id}" />
</StackPanel>
<StackPanel Orientation="Vertical" Margin="0,20,0,0">
<TextBlock Text="Name"/>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</StackPanel>
</ListBox>
</Grid>
How can i bind the items in the code behind?
And how can i manage the "click" on the list?
In a Page.xaml i want to write something like
<LUC:ListboxInUserControl x:Name="MyListbox DataContext="{Binding}"/>
and in the code behind
private ObservableCollection<Truck> TestList { get; set; }
...
TestList = await TruckService.GetAll(); //a method to get the list
MyListbox.MyItemsSource = TestList;
Add listbox to your UserControl,
<ListBox x:Name="aName" SelectionChanged="aName_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Vertical" Margin="0,20,0,0">
<TextBlock Text="Id"/>
<TextBlock Text="{Binding Id}" />
</StackPanel>
<StackPanel Orientation="Vertical" Margin="0,20,0,0">
<TextBlock Text="Name"/>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Add event handler for getting selection changed and public property of listbox to bind objects in user control code behind,
public event EventHandler<EventArgs> SelectionChangedEvent;
public ListBoxInUserControl()
{
this.InitializeComponent();
}
private void aName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectionChangedEvent(sender, new EventArgs());
}
private ListBox myVar;
public ListBox MyProperty
{
get { return aName; }
set { aName = value; }
}
Then you can add this usercontrol in your xaml,
<local:ListBoxInUserControl x:Name="uc_ListBoxInUserControl" SelectionChangedEvent="uc_ListBoxInUserControl_SelectionChangedEvent"> </local:ListBoxInUserControl>
In code behind you can bind data ,
uc_ListBoxInUserControl.MyProperty.ItemsSource = TestList;
and access selection changed event,
private void uc_ListBoxInUserControl_SelectionChangedEvent(object sender, EventArgs e)
{
}

Retrieve View info from DataTemplateSelector

maybe this is a trivial question for many of you...
My app has a TabControl defined as:
<TabControl ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab}">
<!--Bind the SelectionChanged event of the tab-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedChangedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<!--This is How tab will look-->
<TabControl.ItemTemplate>
<DataTemplate>
<DockPanel>
<Button Name="BtnCloseTab"
DockPanel.Dock="Right"
Margin="5,0,0,0"
Padding="0"
Command="{Binding RelativeSource=
{RelativeSource FindAncestor, AncestorType={x:Type TabControl}},
Path=DataContext.CloseTabCommand}">
<Image Source="/EurocomCPS;component/Images/closeTab.png" Height="11" Width="11"></Image>
</Button>
<TextBlock Text="{Binding Header}" />
</DockPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<!--This will be the content for the tab control-->
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl
ContentTemplateSelector="{StaticResource TemplateSelector}"
Content="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
In the window ViewModel I have the following prop:
private ObservableCollection<Tab> _Tabs;
public CPSViewModel()
{
_Tabs = new ObservableCollection<Tab>();
}
public ObservableCollection<Tab> Tabs
{
get { return _Tabs;}
private set
{
_Tabs = value;
this.RaisePropertyChanged("Tabs");
}
}
Now, when a new Tab is created, the following DataTemplateSelector is called:
class TemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item != null)
{
string templateFile = string.Format("Templates/{0}",
Properties.Settings.Default.AppId + ".tmpl");
if (File.Exists(templateFile))
{
FileStream fs = new FileStream(templateFile, FileMode.Open);
DataTemplate template = XamlReader.Load(fs) as DataTemplate;
return template;
}
}
return null;
}
}
The DataTemplate is based on the XmlDataProvider and here I need to "inform" the Template which xml file it has to load because it is different for every tab:
<DataTemplate
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate.Resources>
<local:StringToBoolConverter x:Key="StringToBoolConverter" />
<local:StringToIntConverter x:Key="StringToIntConverter" />
<XmlDataProvider x:Key="dataProvider" XPath="func/parametri/param/BLOCKS"/>
</DataTemplate.Resources>
<Grid>
.... controls ....
</Grid>
</DataTemplate>
Is there a way to do it?
EDIT
Substantially what I have to do is to have access to my Tab class into the TemplateSelector.
Regards,
Daniele.
if you could define your tabs like
public class TabFirst:ITab {}
public class TabSecond:ITab {}
public class TabBlup:ITab {}
viewmodel
public ObservableCollection<ITab> Tabs
{
get { return _Tabs;}
private set
{
_Tabs = value;
this.RaisePropertyChanged("Tabs");
}
}
you could get rid of the DataTemplateSelector and just definfe your datatemplates in your resources
<DataTemplate DataType="{x:Type local:TabFirst}">
<view:TabFirstView />
<DataTemplate/>
<DataTemplate DataType="{x:Type local:TabSecond}">
<view:TabSecondView />
<DataTemplate/>
and your content control would be just
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>

Categories

Resources