WPF MVVM - 选项卡式界面未按预期工作

本文关键字:工作 界面 MVVM 选项 WPF | 更新日期: 2023-09-27 18:36:53

第一:我是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>

视图模型是这样的:

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());
    }

各种其他按钮将不同的视图模型添加到工作区集合中。

我定义了多个数据模板(每个视图模型一个)。这是其中之一:

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

WorkspaceView模型是这样的:

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
}
}

例如产品视图模型

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,然后所有输入的信息都消失了(对象为 null)。

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

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

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

最好

斯特凡

WPF MVVM - 选项卡式界面未按预期工作

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

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

并将其绑定到选项卡控件上的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;
    }