繁体   English   中英

使用TabControl和MVVM在WPF中进行正确的数据绑定

[英]Proper databinding in WPF using TabControl and MVVM

就在我以为自己在这方面变得更好的时候,TabControl现在给我带来了问题。 我已经阅读了StackOverflow上的相关文章,但是无法使我的简单演示应用程序按我希望的方式工作。

为了使事情集中注意力,我将从一个关于我不了解的事情的单一问题开始。

我有一个TabControl,其TabItems各自承载相同的UserControl。 当我将TabControl.ContentTemplate的DataTemplate设置为UserControl时,将显示该控件的呈现,但看起来每个标签的控件都是相同的。 也许它根本不与任何选项卡绑定。

MainWindow.xaml

<Window x:Class="TabControlMvvm.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:localviews="clr-namespace:TabControlMvvm.Views"
    Title="MainWindow" Height="350" Width="525">
    <TabControl ItemsSource="{Binding Tabs}" SelectedIndex="{Binding Selected}">
        <TabControl.ContentTemplate>
            <DataTemplate>
               <localviews:PersonMainPanel />
            </DataTemplate>
        </TabControl.ContentTemplate>        
    </TabControl>
</Window>

后面的代码只是将ViewModel设置为其DataContext:

namespace TabControlMvvm {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new TabControlMvvm.ViewModels.MainViewModel();
        }
    }
}

TabItem的Content应该是另一个UserControl PersonMainPanel.xaml:

<UserControl x:Class="TabControlMvvm.Views.PersonMainPanel"
             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" 
             xmlns:localviews="clr-namespace:TabControlMvvm.Views"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Border BorderBrush="Red" BorderThickness="2">
        <TabControl TabStripPlacement="Bottom">
            <TabItem Header="Tab 1">
                <localviews:MyTabItem />
            </TabItem>
            <TabItem Header="Tab 2">
                <TextBlock Text="This was left blank intentionally" />
            </TabItem>
            <TabItem Header="Tab 3">
                <TextBlock Text="This was also left blank intentionally" />
            </TabItem>
        </TabControl>
    </Border>
</UserControl>    

后台代码:

namespace TabControlMvvm.Views {
    /// <summary>
    /// Interaction logic for PersonMainPanel.xaml
    /// </summary>
    public partial class PersonMainPanel : UserControl {
        public PersonMainPanel()
        {
            InitializeComponent();
        }
    }
}

和MainViewModel:

namespace TabControlMvvm.ViewModels {
    public class MainViewModel : ViewModelBase {
        public ICollectionView Tabs { get; set; }
        public int Selected { get; set; }

        public class Person
        {
            public string Name { get; set; }
        }

        public class DummyController {
            public List<Person> Persons { get; private set; }

            public DummyController()
            {
                Persons = new List<Person> {
                    new Person { Name = "Larry" },
                    new Person { Name = "Darryl" },
                    new Person { Name = "Other brother Darryl" }
                };
            }
        }

        public DummyController Controller { get; private set; }

        public RelayCommand HelloCommand { get; set; }

        public MainViewModel()
        {
            Controller = new DummyController();

            /*
            IEnumerable<TabItem> tabs = Enumerable.Range( 1, _controller.Persons.Count())
                                                  .Select( x => new TabItem { Header = String.Format( "Person {0}", x),
                                                                              Content = new PersonMainPanel() });
             */
            IEnumerable<TabItem> tabs = Enumerable.Range( 1, Controller.Persons.Count())
                                                  .Select( x => new TabItem { Header = String.Format( "Person {0}", x)});
            Tabs = CollectionViewSource.GetDefaultView( tabs.ToList());
            Tabs.MoveCurrentToFirst();

            InitializeCommands();
        }

        private void InitializeCommands()
        {
            HelloCommand = new RelayCommand( () => { MessageBox.Show( String.Format( "Hello, Person {0} named {1}!", 
                                                                      Selected, Controller.Persons[Selected].Name)); });
        }
    }
}

PersonMainPanel承载另一个TabControl,其中Tab 1的Content为MyTabItem.xaml:

<UserControl x:Class="TabControlMvvm.Views.MyTabItem"
             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="300" d:DesignWidth="300">
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Name:" />
            <TextBox Text="{Binding Name}" Width="100" />            
        </StackPanel>
        <Button Command="{Binding HelloCommand}" Content="Say Hello" />
    </StackPanel>
</UserControl>

后台代码:

namespace TabControlMvvm.Views {
    /// <summary>
    /// Interaction logic for MyTabItem.xaml
    /// </summary>
    public partial class MyTabItem : UserControl {
        public MyTabItem()
        {
            InitializeComponent();
        }
    }
}

在运行时看起来像这样:

在此处输入图片说明

到目前为止,我遇到的问题:

  1. 当我输入“人员1”的名称,然后单击“人员2”选项卡时,“人员1”的名称仍然可见,因此我假设控件未正确绑定数据。 我知道ItemsControls不会将其DataContext传递给其子级,但是我不确定如何在不将View关联到后台代码的情况下解决此问题。
  2. 由于缺少DataContext,我本来希望在“输出”窗口中出现数据绑定错误,但是我没有任何错误。 我假设DataContext为null,但这是否仍会导致绑定错误?
  3. 如何有效使用Snoop来调试此类问题?

这是示例解决方案: http : //www.filedropper.com/tabcontrolmvvm

这是解决方案:

在MainWindow中,修改您的TabControl模板,以绑定来自模型的Header:

<TabControl ItemsSource="{Binding Tabs}" SelectedIndex="{Binding Selected}">
    <TabControl.ContentTemplate>
        <DataTemplate>
            <localviews:PersonMainPanel />
        </DataTemplate>
    </TabControl.ContentTemplate>
    <TabControl.ItemContainerStyle>
        <Style TargetType="TabItem">
            <Setter Property="Header" Value="{Binding Header}"/>
        </Style>
    </TabControl.ItemContainerStyle>
</TabControl>

在MyTabItem.xaml中,设置UpdateTrigger,因为默认情况下“ OnLostFocus”有时无法保存您的数据:

 <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Width="100" />       

在MainViewModel中,修改标签的创建,使其也具有Name属性:

IEnumerable<TabItem> tabs = Enumerable.Range( 1, Controller.Persons.Count())
                                        .Select( x => new TabItem { Header = String.Format("Person {0}", x), Name = Controller.Persons[x-1].Name });

同样,最重要的是,创建自己的TabItem类以包含一些有界数据:

public class TabItem
{
    public string Name { set; get; }
    public string Header { set; get; }    
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM