Silverlight 如何在MVVM灯光下关闭选项卡项
在我看来,我正在动态添加自定义选项卡项(TextseiteTabItem)。使用属性DataContext,我为每个TabItem提供了一个模型(填充值)。现在我在自定义选项卡Items中添加了一个close命令,但它不起作用。我无法将关闭命令发送到viewmodel。以上是我的尝试 我的自定义选项卡项:Silverlight 如何在MVVM灯光下关闭选项卡项,silverlight,events,xaml,binding,mvvm,Silverlight,Events,Xaml,Binding,Mvvm,在我看来,我正在动态添加自定义选项卡项(TextseiteTabItem)。使用属性DataContext,我为每个TabItem提供了一个模型(填充值)。现在我在自定义选项卡Items中添加了一个close命令,但它不起作用。我无法将关闭命令发送到viewmodel。以上是我的尝试 我的自定义选项卡项: <sdk:TabItem x:Class="PortfolioCreator.TextseiteTabItem" xmlns="http://schemas.mi
<sdk:TabItem x:Class="PortfolioCreator.TextseiteTabItem"
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"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<sdk:TabItem.Header>
<StackPanel Orientation="Horizontal">
<sdk:Label Content="{Binding Seitennummer, StringFormat='Seite {0}', Mode=TwoWay}"/>
<Button Content="X"
Command="{Binding CloseTabCommand, Mode=TwoWay}"
DataContext="{Binding ElementName=TemplateTabControl}"
CommandParameter="{Binding SelectedItem, ElementName=TemplateTabControl}" />
</StackPanel>
</sdk:TabItem.Header>
<sdk:TabItem.Content>
<Grid x:Name="LayoutRoot">
...
</Grid>
</sdk:TabItem.Content>
</sdk:TabItem>
...
我认为:
...
<sdk:TabControl toolkit:DockPanel.Dock="Bottom" ItemsSource="{Binding Tabs}" x:Name="TemplateTabControl"/>
...
。。。
...
在我的ViewModel中:
public class PortfolioViewModel : ViewModelBase
{
public ObservableCollection<TabItem> Tabs { get; set; }
public RelayCommand<TabItem> CloseTabCommand
{
get;
private set;
}
public PortfolioViewModel()
{
CloseTabCommand = new RelayCommand<TabItem>(tab =>
{
//never reached
},
tab =>
{
//never reached
});
Tabs = new ObservableCollection<TabItem>();
AddTextseite();
AddTextseite();
}
void AddTextseite()
{
TabItem item = new TextseiteTabItem();
item.DataContext = new TextSeiteModel();
Tabs.Add(item);
}
}
public delegate void CloseTabItemHandler();
public class TextseiteTabItemViewModel : ViewModelBase
{
public event CloseTabItemHandler CloseTabItem;
public RelayCommand CloseTabCommand {get; set;}
public TextseiteTabItemViewModel()
{
CloseTabCommand = new RelayCommand(() =>
{
if (CloseTabItem == null) return;
CloseTabItem();
});
}
}
public class PortfolioViewModel : ViewModelBase
{
public ObservableCollection<TabItem> Tabs { get; set; }
public PortfolioViewModel()
{
Tabs = new ObservableCollection<TabItem>();
AddTextseite();
AddTextseite();
}
void AddTextseite()
{
var viewmodel = new TextseiteTabItemViewModel();
TabItem item = new TextseiteTabItemView();
item.DataContext = viewmodel;
viewmodel.CloseTabItem += new CloseTabItemHandler(() =>
{
Tabs.Remove(item);
});
Tabs.Add(item);
}
}
public类PortfolioViewModel:ViewModelBase
{
公共ObservableCollection选项卡{get;set;}
公共RelayCommand closetab命令
{
得到;
私人设置;
}
公共PortfolioViewModel()
{
CloseTabCommand=新的RelayCommand(选项卡=>
{
//从未达到
},
选项卡=>
{
//从未达到
});
Tabs=新的ObservableCollection();
AddTextseite();
AddTextseite();
}
void AddTextseite()
{
TabItem item=新文本seitetabitem();
item.DataContext=newtextseitemodel();
添加(项目);
}
}
您正试图使用MVVM,但我看到的奇怪之处是视图模型中的ui元素(选项卡)集合。正确的方法是创建描述选项卡项的ViewModel,并将命令移到那里。然后它就会绑定。若要从选项卡中删除选项卡,应在选项卡视图模型中公开事件,并从PortfolioViewModel附加到该事件
当然,我的更改将导致您的TextSeiteTableItem不会显示在TablControl中。但是它可以通过TabControl.ItemTemplate和TabControl.ContentTemplate轻松修复。首先,您的
CloseTabCommand
在当前代码段中不起任何作用://从未到达。执行处理程序的内容应类似于tab.Visibility=Visibility.Collapsed
或myTabControl.Items.Remove(myTabItem)
其次,正如@Rafal所指出的,在ViewModel中使用UI元素不是实现MVVM的正确方法。如果您想要可关闭的选项卡项,正确的方法是派生一个通用的CloseableTabItem
控件,或者在UI层上编写一个ClosableTabItemBehavior
,该控件使用一个可设置的ICommand CloseCommand
,可以绑定到ViewModel上相应的ICommand
实例。诚然,对于您的项目来说,这种方法可能过于复杂。您发现了一个带有wpf可关闭选项卡的演示应用程序,也许它也适用于您的silverlight版本。这是我解决此问题的方法。我承认这不是一个好的解决方案,打破了mvvm模式,但正如@herzmeister所说,其他方法对于我的项目来说太复杂了。(但这不是最终的解决方案;-))
选项卡项视图模型:
public class PortfolioViewModel : ViewModelBase
{
public ObservableCollection<TabItem> Tabs { get; set; }
public RelayCommand<TabItem> CloseTabCommand
{
get;
private set;
}
public PortfolioViewModel()
{
CloseTabCommand = new RelayCommand<TabItem>(tab =>
{
//never reached
},
tab =>
{
//never reached
});
Tabs = new ObservableCollection<TabItem>();
AddTextseite();
AddTextseite();
}
void AddTextseite()
{
TabItem item = new TextseiteTabItem();
item.DataContext = new TextSeiteModel();
Tabs.Add(item);
}
}
public delegate void CloseTabItemHandler();
public class TextseiteTabItemViewModel : ViewModelBase
{
public event CloseTabItemHandler CloseTabItem;
public RelayCommand CloseTabCommand {get; set;}
public TextseiteTabItemViewModel()
{
CloseTabCommand = new RelayCommand(() =>
{
if (CloseTabItem == null) return;
CloseTabItem();
});
}
}
public class PortfolioViewModel : ViewModelBase
{
public ObservableCollection<TabItem> Tabs { get; set; }
public PortfolioViewModel()
{
Tabs = new ObservableCollection<TabItem>();
AddTextseite();
AddTextseite();
}
void AddTextseite()
{
var viewmodel = new TextseiteTabItemViewModel();
TabItem item = new TextseiteTabItemView();
item.DataContext = viewmodel;
viewmodel.CloseTabItem += new CloseTabItemHandler(() =>
{
Tabs.Remove(item);
});
Tabs.Add(item);
}
}
选项卡项视图:
<sdk:TabItem x:Class="PortfolioCreator.TextseiteTabItemView"
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"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<sdk:TabItem.Header>
<StackPanel Orientation="Horizontal">
<Button Content="X" Command="{Binding CloseTabCommand, Mode=TwoWay}" />
</StackPanel>
</sdk:TabItem.Header>
<sdk:TabItem.Content>
<Grid x:Name="LayoutRoot">
...
</Grid>
</sdk:TabItem.Content>
</sdk:TabItem>
...
父视图模型:
public class PortfolioViewModel : ViewModelBase
{
public ObservableCollection<TabItem> Tabs { get; set; }
public RelayCommand<TabItem> CloseTabCommand
{
get;
private set;
}
public PortfolioViewModel()
{
CloseTabCommand = new RelayCommand<TabItem>(tab =>
{
//never reached
},
tab =>
{
//never reached
});
Tabs = new ObservableCollection<TabItem>();
AddTextseite();
AddTextseite();
}
void AddTextseite()
{
TabItem item = new TextseiteTabItem();
item.DataContext = new TextSeiteModel();
Tabs.Add(item);
}
}
public delegate void CloseTabItemHandler();
public class TextseiteTabItemViewModel : ViewModelBase
{
public event CloseTabItemHandler CloseTabItem;
public RelayCommand CloseTabCommand {get; set;}
public TextseiteTabItemViewModel()
{
CloseTabCommand = new RelayCommand(() =>
{
if (CloseTabItem == null) return;
CloseTabItem();
});
}
}
public class PortfolioViewModel : ViewModelBase
{
public ObservableCollection<TabItem> Tabs { get; set; }
public PortfolioViewModel()
{
Tabs = new ObservableCollection<TabItem>();
AddTextseite();
AddTextseite();
}
void AddTextseite()
{
var viewmodel = new TextseiteTabItemViewModel();
TabItem item = new TextseiteTabItemView();
item.DataContext = viewmodel;
viewmodel.CloseTabItem += new CloseTabItemHandler(() =>
{
Tabs.Remove(item);
});
Tabs.Add(item);
}
}
public类PortfolioViewModel:ViewModelBase
{
公共ObservableCollection选项卡{get;set;}
公共PortfolioViewModel()
{
Tabs=新的ObservableCollection();
AddTextseite();
AddTextseite();
}
void AddTextseite()
{
var viewmodel=新文本seitetabitemviewmodel();
TabItem=newtextseitetabitemview();
item.DataContext=viewmodel;
viewmodel.CloseTabItem+=新的CloseTabItemHandler(()=>
{
选项卡。删除(项目);
});
添加(项目);
}
}
将TabControl.ItemsSource绑定到模型集合。将模型添加到集合时,每个选项卡都会显示。删除模型时,每个选项卡都会消失。VM不应该做任何UI工作。