Silverlight 如何在MVVM灯光下关闭选项卡项

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

在我看来,我正在动态添加自定义选项卡项(TextseiteTabItem)。使用属性DataContext,我为每个TabItem提供了一个模型(填充值)。现在我在自定义选项卡Items中添加了一个close命令,但它不起作用。我无法将关闭命令发送到viewmodel。以上是我的尝试

我的自定义选项卡项:

<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工作。