【问题标题】:Wpf MVVM - Tabbed interface is not working as expectedWpf MVVM - 选项卡式界面未按预期工作
【发布时间】:2013-10-26 08:27:37
【问题描述】:

首先:我是 MVVM 和 WPF 的新手。

我正在尝试创建一个带有选项卡式用户界面的小应用程序。用户可以使用打开新 TabItem 的按钮来创建产品和存储位置。

我在视图中的代码如下所示:

<TabControl ItemsSource="{Binding Workspaces}"
        IsSynchronizedWithCurrentItem="True"
        Margin="3"
        DockPanel.Dock="Top">
  <TabControl.ItemTemplate>
       <DataTemplate>
          <Label Content="{Binding DisplayName}" />
       </DataTemplate>
  </TabControl.ItemTemplate>
</TabControl>

View Model 是这样的:

ObservableCollection<WorkspaceViewModel> _workspaces;
    public ObservableCollection<WorkspaceViewModel> Workspaces
    {
        get
        {
            if (_workspaces == null)
            {
                _workspaces = new ObservableCollection<WorkspaceViewModel>();
            }
            return _workspaces;
        }
        set
        {
            _workspaces = value;

        }
    }
public void AddProduct(object obj)
    {
        Workspaces.Add(new ProductViewModel());
    }

各种其他按钮将不同的 ViewModel 添加到 Workspaces 集合中。

我已经定义了多个数据模板(每个 ViewModel 一个)。这是一个:

<DataTemplate DataType="{x:Type vm:ProductViewModel}">
    <vw:ProductView />
</DataTemplate>

WorkspaceViewModel 是这样的:

namespace Inventory.Desktop.ViewModels
{
public abstract class WorkspaceViewModel : INotifyPropertyChanged
{
    #region Events and EventHandlers

    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion
}
}

例如 ProductViewModel

namespace Inventory.Desktop.ViewModels
{
public class ProductViewModel: WorkspaceViewModel
{
    private Product _product;
    private string _displayName;


    public string DisplayName
    {
        get
        {
            if (String.IsNullOrEmpty(_displayName))
            {
                return "Neues Produkt";
            } else
            {
                return _displayName;
            }
        }
        set
        {
            _displayName = value;
            NotifyPropertyChanged("DisplayName");
        }
    }


    #region Public Properties

    public Product Product
    {
        get
        { 
            return _product; 
        }
        set
        {
            _product = value;
            NotifyPropertyChanged("Product");
        }
    }

    public string Title
    {
        get
        {
            return _product.Title;
        }
        set
        {
            _product.Title = value;
            NotifyPropertyChanged("Title");
        }
    }

    public string ScanCode
    {
        get
        {
            return _product.ScanCode;
        }
        set
        {
            _product.ScanCode = value;
            NotifyPropertyChanged("ScanCode");
        }
    }

    public string Manufacturer
    {
        get
        {
            return _product.Manufacturer;
        }
        set
        {
            _product.Manufacturer = value;
            NotifyPropertyChanged("Manufacturer");
        }
    }

    public string ManufacturerNumber
    {
        get
        {
            return _product.ManufacturerNumber;
        }
        set
        {
            _product.ManufacturerNumber = value;
            NotifyPropertyChanged("ManufacturerNumber");
        }
    }

    public string Description
    {
        get
        {
            return _product.Description;
        }
        set
        {
            _product.Description = value;
            NotifyPropertyChanged("Description");
        }
    }

    #endregion

    #region Commands

    private ICommand _saveCommand;

    public ICommand SaveCommand
    {
        get
        {
            return _saveCommand;
        }
        set
        {
            _saveCommand = value;
        }
    }

    #endregion

    #region Command Executions

    public void Save(object obj)
    {

        using (var db = new InvContext())
        {
            db.Products.Attach(Product);
            db.Entry(Product).State = Product.ProductId == 0 ?
                EntityState.Added : EntityState.Modified;
            db.SaveChanges();
        }

        MessageBox.Show("Product saved: " + Product.Title);
    }

    #endregion

    #region Constructors

    public ProductViewModel()
    {
        if (_product == null)
        {
            _product = new Product();
        }

        SaveCommand = new RelayCommand(new Action<object>(Save));
    }

    #endregion


}
}

这里是ProductView.xaml 视图:

<UserControl x:Class="Inventory.Desktop.Views.ProductView"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="400" d:DesignWidth="450">
<DockPanel>
    <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" FlowDirection="RightToLeft">
        <Button Name="SaveProductButton" Command="{Binding SaveCommand}" Content="Speichern" Margin="3" BorderThickness="0">
        </Button>
    </StackPanel>
    <StackPanel DockPanel.Dock="Top" VerticalAlignment="Stretch">
        <Label Content="Scan Code" />
        <TextBox Text="{Binding Path=ScanCode}" HorizontalAlignment="Stretch" Margin="3" Padding="3" Height="50" TextAlignment="Right">
            <TextBox.Background>
                <ImageBrush ImageSource="..\Images\Barcode32.png" AlignmentX="Left" Stretch="None" />
            </TextBox.Background>
        </TextBox>
        <Label Content="Bezeichnung" />
        <TextBox Text="{Binding Path=Title, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
        <Label Content="Hersteller" />
        <TextBox Text="{Binding Path=Manufacturer, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
        <Label Content="Hersteller Nummer" />
        <TextBox Text="{Binding Path=ManufacturerNumber, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
        <Label Content="Beschreibung / Information" />
        <TextBox Text="{Binding Path=Description, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
    </StackPanel>

</DockPanel>
</UserControl>

这里是代码隐藏ProductView.xaml.cs

namespace Inventory.Desktop.Views
{
/// <summary>
/// Interaktionslogik für ProductView.xaml
/// </summary>
public partial class ProductView : UserControl
{

    ProductViewModel _productModel = new ProductViewModel();

    public ProductView()
    {
        InitializeComponent();
        base.DataContext = _productModel;
    }
}
}

目前的工作:

  • 当我单击一个按钮时,我得到了一个新的 TabItem,它显示了正确的视图并且所有命令都正常工作。

什么不工作:

  • 当我打开一个 TabItem,输入一些信息,然后我用不同的 ViewModel 打开另一个 TabItem,将焦点切换到新的 TabItem,然后又回到原来的 oen,然后所有输入的信息都没有了(对象为空)。

  • 当我打开一个 TabItem 时,输入一些信息,然后我打开另一个具有相同 ViewModel 的 TabItem,然后两个 TabItem 显示相同的信息。

  • 当我添加一个新的 TabItem 时,它没有获得焦点。

我完全迷失了,我希望你能告诉我我做错了什么。

最好的

斯蒂芬

【问题讨论】:

  • 看来问题出在你的 WorkspaceViewModel 上,你能发布一些关于 WorkspaceViewModel 的信息吗?
  • 你好穆贾希德。我编辑了我的问题并包含了模型
  • 尝试在文本框绑定中使用 UpdateSourceTrigger=PropertyChanged
  • 对于您的问题 3,sthotakura 的解决方案将起作用
  • 你好穆贾希德。谢谢你的建议。我添加了 UpdateSourceTrigger,但没有任何效果。我将在后面添加我的 ProductView xaml 和代码。我相信设置数据上下文可能与我的问题有关?借助 skthotakura,问题 3 现在得到了解决

标签: c# wpf mvvm tabcontrol


【解决方案1】:

在您的 ViewModel 上有一个属性来存储对当前/选定选项卡的引用

public WorkspaceViewModel SelectedTab
{
    get { return _selectedTab; }
    set
    {
        _selectedTab = value;
        RaisePropertyChanged(() => SelectedTab);
    }
}

并将其绑定到 TabControl 上的 SelectedItem 属性。

<TabControl ItemsSource="{Binding Workspaces}"
        SelectedItem="{Binding SelectedTab, Mode=TwoWay}"
        Margin="3"
        DockPanel.Dock="Top">
  <TabControl.ItemTemplate>
       <DataTemplate>
          <Label Content="{Binding DisplayName}" />
       </DataTemplate>
  </TabControl.ItemTemplate>
</TabControl>

最后,您希望在添加新标签时更新SelectedTab 属性。像这样修改你的AddProduct

    public void AddProduct(object obj)
    {
        var workspace = new ProductViewModel();
        Workspaces.Add(workspace);
        SelectedTab = workspace;
    }

【讨论】:

  • 谢谢。这解决了我的一个问题,我现在在添加新标签时获得焦点。
  • @Stefan 您是否尝试删除 IsSynchronizedWithCurrentItem="True" 并查看是否可以解决其他两个问题?
猜你喜欢
  • 2020-08-09
  • 2021-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-21
  • 2018-06-27
相关资源
最近更新 更多