使用 mvvm 在选项卡项中动态包含一个关闭按钮
Include a close button in a tab item dynamically using mvvm
我的主窗口中有一个选项卡控件。选项卡控件的 default/first 选项卡是主用户控件。在主页中,我有一个可以添加更多选项卡的按钮。
MainWindow.xaml:
<Window.Resources>
<DataTemplate x:Key="ClosableTabItemTemplate">
<Button Content="X" Cursor="Hand" DockPanel.Dock="Right" Focusable="False"
FontFamily="Courier" FontSize="9" FontWeight="Bold" Margin="0,1,0,0" Padding="0"
VerticalContentAlignment="Bottom" Width="16" Height="16"/>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl Name="tabMain" ItemsSource="{Binding TabItems,UpdateSourceTrigger=PropertyChanged}" />
</Grid>
在我的视图模型中,我有添加新选项卡的添加功能。我需要所有这些新添加的选项卡的关闭按钮。
public MainViewModel()
{
try
{
Home Item2 = new Home();
TabItems.Add(new TabItem() { Header = "Home", Content = Item2 });
}
catch(Exception ex)
{
MessageBox.Show("Exception "+ex);
}
//Function to add new tabs.
public void AddNewTabs()
{
ChildWindow childContent = new ChildWindow();
TabItem item = new TabItem() { Header = "New Tab", Content = childContent};
item.MouseDoubleClick += new MouseButtonEventHandler(tab_MouseDoubleClick);
TabItems.Add(item);
}
现在正在添加新标签,但没有关闭按钮。我试过给
item.HeaderTemplate = FindResource("ClosableTabItemTemplate") as DataTemplate;
但是显示错误
如有任何帮助,我们将不胜感激。
提前致谢。
为了遵守 Mvvm 模式,您的视图模型不得直接与视图交互,这意味着您需要使用命令而不是事件,不要在视图模型逻辑中使用任何与视图相关的控件..
这里有一个更简洁的方法来实现您正在寻找的东西:
首先在视图中使用TabControl
ContentTemplate
和ItemTemplate
代替:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Button Content="Add new tab" Command="{Binding AddNewTabCommand}"></Button>
<TabControl Grid.Row="1" Name="TabMain" ItemsSource="{Binding TabItems,UpdateSourceTrigger=PropertyChanged}" >
<TabControl.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Header}"/>
<Button Content="X" Cursor="Hand" DockPanel.Dock="Right" Focusable="False"
FontFamily="Courier" FontSize="9" FontWeight="Bold" Margin="0,1,0,0" Padding="0"
VerticalContentAlignment="Bottom" Width="16" Height="16" Command="{Binding DataContext.CloseTabCommand,RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding ElementName=TabMain,Path=SelectedItem}"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Content}" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
其次,在 ViewModel 中创建一个 class TabItem,它将包含一个选项卡内容和 header(根据需要自定义),您可能需要如果 class 反映了对视图的任何更改,则实施 INotifyPropertyChanged
接口,
第三,定义从 TabItems ObservableCollection
、
添加和移除 TabItem 的命令
这里是 viewModel 代码:
public class TabItem
{
public String Header { get; set; }
public String Content { get; set; }
}
public class MainViewModel : INotifyPropertyChanged
{
private ObservableCollection<TabItem> _tabItems;
public ObservableCollection<TabItem> TabItems
{
get
{
return _tabItems;
}
set
{
if (_tabItems == value)
{
return;
}
_tabItems = value;
OnPropertyChanged();
}
}
private RelayCommand _addNewTabCommand;
public RelayCommand AddNewTabCommand
{
get
{
return _addNewTabCommand
?? (_addNewTabCommand = new RelayCommand(
() =>
{
TabItems.Add(new TabItem()
{
Header = "NewTab",
Content = "NewContent"
});
}));
}
}
private RelayCommand<TabItem> _closeTabCommand;
public RelayCommand<TabItem> CloseTabCommand
{
get
{
return _closeTabCommand
?? (_closeTabCommand = new RelayCommand<TabItem>(
(t) =>
{
TabItems.Remove(t);
}));
}
}
public MainViewModel()
{
TabItems = new ObservableCollection<TabItem>()
{
new TabItem()
{
Header = "Home",
Content = "Home Content"
},
new TabItem()
{
Header = "Header1",
Content = "Content1"
}
};
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
输出:
Ps:我的 MainWindow
视图 DataContext
设置为 MainWindowViewModel
,这就是为什么我使用 AncestorType
来查找 CloseTabCommand
您可以查看 Dragablz 的功能以及更多功能。
免责声明:这是我的库,但它是开源的,请尽情享受吧。
我的主窗口中有一个选项卡控件。选项卡控件的 default/first 选项卡是主用户控件。在主页中,我有一个可以添加更多选项卡的按钮。
MainWindow.xaml:
<Window.Resources>
<DataTemplate x:Key="ClosableTabItemTemplate">
<Button Content="X" Cursor="Hand" DockPanel.Dock="Right" Focusable="False"
FontFamily="Courier" FontSize="9" FontWeight="Bold" Margin="0,1,0,0" Padding="0"
VerticalContentAlignment="Bottom" Width="16" Height="16"/>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl Name="tabMain" ItemsSource="{Binding TabItems,UpdateSourceTrigger=PropertyChanged}" />
</Grid>
在我的视图模型中,我有添加新选项卡的添加功能。我需要所有这些新添加的选项卡的关闭按钮。
public MainViewModel()
{
try
{
Home Item2 = new Home();
TabItems.Add(new TabItem() { Header = "Home", Content = Item2 });
}
catch(Exception ex)
{
MessageBox.Show("Exception "+ex);
}
//Function to add new tabs.
public void AddNewTabs()
{
ChildWindow childContent = new ChildWindow();
TabItem item = new TabItem() { Header = "New Tab", Content = childContent};
item.MouseDoubleClick += new MouseButtonEventHandler(tab_MouseDoubleClick);
TabItems.Add(item);
}
现在正在添加新标签,但没有关闭按钮。我试过给
item.HeaderTemplate = FindResource("ClosableTabItemTemplate") as DataTemplate;
但是显示错误
如有任何帮助,我们将不胜感激。
提前致谢。
为了遵守 Mvvm 模式,您的视图模型不得直接与视图交互,这意味着您需要使用命令而不是事件,不要在视图模型逻辑中使用任何与视图相关的控件..
这里有一个更简洁的方法来实现您正在寻找的东西:
首先在视图中使用TabControl
ContentTemplate
和ItemTemplate
代替:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Button Content="Add new tab" Command="{Binding AddNewTabCommand}"></Button>
<TabControl Grid.Row="1" Name="TabMain" ItemsSource="{Binding TabItems,UpdateSourceTrigger=PropertyChanged}" >
<TabControl.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Header}"/>
<Button Content="X" Cursor="Hand" DockPanel.Dock="Right" Focusable="False"
FontFamily="Courier" FontSize="9" FontWeight="Bold" Margin="0,1,0,0" Padding="0"
VerticalContentAlignment="Bottom" Width="16" Height="16" Command="{Binding DataContext.CloseTabCommand,RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding ElementName=TabMain,Path=SelectedItem}"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Content}" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
其次,在 ViewModel 中创建一个 class TabItem,它将包含一个选项卡内容和 header(根据需要自定义),您可能需要如果 class 反映了对视图的任何更改,则实施 INotifyPropertyChanged
接口,
第三,定义从 TabItems ObservableCollection
、
这里是 viewModel 代码:
public class TabItem
{
public String Header { get; set; }
public String Content { get; set; }
}
public class MainViewModel : INotifyPropertyChanged
{
private ObservableCollection<TabItem> _tabItems;
public ObservableCollection<TabItem> TabItems
{
get
{
return _tabItems;
}
set
{
if (_tabItems == value)
{
return;
}
_tabItems = value;
OnPropertyChanged();
}
}
private RelayCommand _addNewTabCommand;
public RelayCommand AddNewTabCommand
{
get
{
return _addNewTabCommand
?? (_addNewTabCommand = new RelayCommand(
() =>
{
TabItems.Add(new TabItem()
{
Header = "NewTab",
Content = "NewContent"
});
}));
}
}
private RelayCommand<TabItem> _closeTabCommand;
public RelayCommand<TabItem> CloseTabCommand
{
get
{
return _closeTabCommand
?? (_closeTabCommand = new RelayCommand<TabItem>(
(t) =>
{
TabItems.Remove(t);
}));
}
}
public MainViewModel()
{
TabItems = new ObservableCollection<TabItem>()
{
new TabItem()
{
Header = "Home",
Content = "Home Content"
},
new TabItem()
{
Header = "Header1",
Content = "Content1"
}
};
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
输出:
Ps:我的 MainWindow
视图 DataContext
设置为 MainWindowViewModel
,这就是为什么我使用 AncestorType
来查找 CloseTabCommand
您可以查看 Dragablz 的功能以及更多功能。
免责声明:这是我的库,但它是开源的,请尽情享受吧。