I have multiple tabitem's (tab1, tab2, ... etc) in a WPF tabcontrol. When I click a button I want to restart the app and open a specific tabitem, say tab2 (in MVVM friendly way).
To restart the app I use
Process.Start(Application.ResourceAssembly.Location);
Application.Current.Shutdown();
But how do I specify which tabitem to display after restart?
CodePudding user response:
Obiously, your new app instance needs to "know something" about the requirement to open a specific tab item.
There are other possiblities like creating a configuration file, but probably a command line parameter will serve well here. To start the new instance you may use something like Process.Start(Application.ResourceAssembly.Location, "/StartTab=MyTab3");
Then, in your ViewModel, have a string property like public string SelectedTabName {get; set;}
and initialize that during the construction of the VM:
var tabInfo = Environment.GetCommandLineArgs().FirstOrDefault(a => a.ToLower().StartsWith("/starttab="));
if (!string.IsNullOrWhiteSpace(tabInfo))
{
SelectedTabName = tabInfo.Substring(tabInfo.IndexOf('=') 1);
}
Finally, in the XAML code, bind the IsSelected property of your tab items to the SelectedTabName string, with the help of a StringToBooleanConverter, using the ConverterParameter like the name of the tab.
public class StringMatchesParameterToBooleanConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
{
if (value is not string val)
return false;
if (parameter is not string param)
return false;
return val == param;
}
[ExcludeFromCodeCoverage]
public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
{
throw new NotImplementedException();
}
}
Your TabControl Xaml code could work like so
<TabControl>
<TabControl.Resources>
<converters:StringMatchesParameterToBooleanConverter x:Key="StringMatchesParameterToBooleanConverter" />
</TabControl.Resources>
<TabItem x:Name="MyTab1"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab1}">
</TabItem>
<TabItem x:Name="MyTab2"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab2}">
</TabItem>
<TabItem x:Name="MyTab3"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab3}">
</TabItem>
</TabControl>
CodePudding user response:
Meanwhile I have done a test project (in VS 2022 using .net 6.0) and it works mostly as described earlier. No problem with throwing an exception in the ConvertBack part of the converter. Only difference required was restarting the program using Environment.ProcessPath instead of Application.ResourceAssembly.Location:
MainWindow.xaml:
<Window x:Class="TestStartWithButtonClick.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:TestStartWithButtonClick"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MainViewModel, IsDesignTimeCreatable=True}"
SizeToContent="WidthAndHeight"
Title="MainWindow" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TabControl Grid.Row="0" Grid.ColumnSpan="3">
<TabControl.Resources>
<local:StringMatchesParameterToBooleanConverter x:Key="StringMatchesParameterToBooleanConverter" />
</TabControl.Resources>
<TabItem x:Name="MyTab1" Header="MyTab1"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab1}">
<StackPanel Width="300" Height="100" />
</TabItem>
<TabItem x:Name="MyTab2" Header="MyTab2"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab2}">
<StackPanel Width="300" Height="100" />
</TabItem>
<TabItem x:Name="MyTab3" Header="MyTab3"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab3}">
<StackPanel Width="300" Height="100" />
</TabItem>
</TabControl>
<Button Grid.Row="1" Grid.Column="0" Margin="0,0,6,0" Content="Start With Tab 1" Command="{Binding StartNewInstanceCommand}" CommandParameter="MyTab1"/>
<Button Grid.Row="1" Grid.Column="1" Margin="6,0,6,0" Content="Start With Tab 2" Command="{Binding StartNewInstanceCommand}" CommandParameter="MyTab2"/>
<Button Grid.Row="1" Grid.Column="2" Margin="6,0,0,0" Content="Start With Tab 3" Command="{Binding StartNewInstanceCommand}" CommandParameter="MyTab3"/>
</Grid>
</Window>
MainWindow.xaml.cs using System.Windows;
namespace TestStartWithButtonClick
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
}
MainViewModel.cs:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
namespace TestStartWithButtonClick
{
public class MainViewModel : INotifyPropertyChanged
{
private string _selectedTabName = string.Empty;
public event PropertyChangedEventHandler? PropertyChanged;
public MainViewModel()
{
var tabInfo = Environment.GetCommandLineArgs().FirstOrDefault(a => a.ToLower().StartsWith("/starttab="));
if (!string.IsNullOrWhiteSpace(tabInfo))
{
SelectedTabName = tabInfo.Substring(tabInfo.IndexOf('=') 1);
}
else
{
SelectedTabName = "MyTab1";
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public StartNewInstanceCommand StartNewInstanceCommand { get; set; } = new();
public string SelectedTabName
{
get => _selectedTabName;
set
{
if (value == _selectedTabName) return;
_selectedTabName = value;
OnPropertyChanged(nameof(SelectedTabName));
}
}
}
public class StartNewInstanceCommand : ICommand
{
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
if (parameter is not string tabItem)
throw new ArgumentException("parameter is not string", nameof(parameter));
Process.Start(Environment.ProcessPath, $"/StartTab={tabItem}");
Application.Current.Shutdown(0);
}
public event EventHandler? CanExecuteChanged;
}
}
StringMatchesParameterToBooleanConverter.cs:
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Windows.Data;
namespace TestStartWithButtonClick
{
public class StringMatchesParameterToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not string val)
return false;
if (parameter is not string param)
return false;
return val == param;
}
[ExcludeFromCodeCoverage]
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}