Dynamically Updating TabController list at Runtime
UPDATE
The solution I present here is not valid anymore. Please take a look in this answer
I had problems redefining the `TabController` length as well.
The solution was to create the TabController
just once in initState
and dispose
it (only once too) - pretend that the TabController length
is just a max length, if you think your app would have a maximum number of 100, use a fixed 100 then.
How to add TabControl content as button dynamically?
You have to replace in XAML
instead of <TabControl.ContentTemplate>
replace it with <TabControl.DataContext>
and that's the solution it takes me hours to find this little mistake.
<TabControl.DataContext>
<DataTemplate>
</DataTemplate>
</TabControl.DataContext>
The above is the change in XAML part.
How to set up a dynamically-populated TabControl with ReactiveUI?
Please refer to the below sample code.
Window1.xaml:
<reactiveui:ReactiveWindow x:Class="WpfApp1.Window1"
x:TypeArguments="local:ViewModel"
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:WpfApp1"
xmlns:reactiveui="http://reactiveui.net"
mc:Ignorable="d"
Title="Window1" Height="450" Width="800">
<Grid>
<TabControl Name="Multitab">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<reactiveui:ViewModelViewHost ViewModel="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</reactiveui:ReactiveWindow>
Window.xaml.cs:
public partial class Window1 : ReactiveWindow<ViewModel>
{
public Window1()
{
InitializeComponent();
ViewModel = new ViewModel();
this.WhenActivated(disposableRegistration =>
{
this.OneWayBind(ViewModel,
viewModel => viewModel.Tabs,
view => view.Multitab.ItemsSource)
.DisposeWith(disposableRegistration);
});
}
}
View Model:
public class ViewModel
{
public ObservableCollection<ITabPage> Tabs { get; } =
new ObservableCollection<ITabPage>() { new MyTabEditorViewModel() };
}
Tab View Model:
public interface ITabPage { }
public class MyTabEditorViewModel : ITabPage
{
public string Name { get; } = "Name...";
}
TabView.xaml:
<reactiveui:ReactiveUserControl x:Class="WpfApp1.TabEditorView"
x:TypeArguments="local:MyTabEditorViewModel"
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:reactiveui="http://reactiveui.net"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock>Tab content...</TextBlock>
</Grid>
</reactiveui:ReactiveUserControl>
TabView.xaml.cs:
public partial class TabEditorView : ReactiveUserControl<MyTabEditorViewModel>
{
public TabEditorView()
{
InitializeComponent();
}
}
Create TabItems with differing content at runtime based on templates in WPF
TabControl.ItemsSource
plus DataTemplates is effectively the "templates as factories" solution you are asking for, but it demands a slightly different approach to your current one.
Rather than writing procedural code to create and template TabItems and calling Items.Add, use the ItemsSource property and data binding. This will cause WPF to create a TabItem for each object in the ItemsSource. You can then use ContentTemplateSelector to select appropriate templates for the object displayed on this tab, according to whatever criteria are appropriate (e.g. the Device property) -- though in this case you will be using DataTemplates rather than ControlTemplates.
Your selector will look something like this:
public class DeviceTypeSelector : DataTemplateSelector
{
public DataTemplate PendantTemplate { get; set; }
public DataTemplate DefaultTemplate { get; set; }
public override SelectTemplate(object item, DependencyObject container)
{
ConfigFile cf = (ConfigFile)item;
switch (cf.Device)
{
case DeviceType.Pendant: return PendantTemplate;
default: return DefaultTemplate;
}
}
}
and will be instantiated in XAML like this:
<local:DeviceTypeSelector x:Key="dts"
PendantTemplate="{StaticResource pt}"
DefaultTemplate="{StaticResource dt}" />
(where pt and dt are suitable DataTemplates defined elsewhere in the resources).
Finally, your TabControl will look like this:
<TabControl Name="EditorTabs"
ContentTemplateSelector="{StaticResource dts}" />
and you set it up as EditorTabs.ItemsSource = myConfigFiles;
(or better still let it acquire the ItemsSource in XAML from the DataContext).
You'll also want to set up the headers of the TabItems: to do this, use TabControl.ItemContainerStyle, with a Setter for the Header property. I think this would look something like this:
<TabControl ...>
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding ConfigurationName}" />
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
(You can also inline the ContentTemplateSelector, by the way: I broke it out into a resource mostly so as to show things in smaller chunks.)
Related Topics
Calling Async Method on Button Click
Ways to Synchronize Interface and Implementation Comments in C#
Serializable Classes and Dynamic Proxies in Ef - How
Mvvm Show New Window from Vm When Seperated Projects
Is There a Complete Iequatable Implementation Reference
Print Rdlc Report Without Showing Reportviewer Control
Singleton Httpclient VS Creating New Httpclient Request
How to Create Custom Http Status Codes
How to Create an Audit Trail with Entity Framework 5 and MVC 4
Determine Client's Computer Name
How to Find Reason of Failed Build Without Any Error or Warning
How to Do Tostring for a Possibly Null Object
How to Iterate Through a Datatable
Do Using Statements and Await Keywords Play Nicely in C#