无法让wpf文本框绑定到我的播放器对象上。

问题描述 投票:0回答:1

我一直在尝试将我的播放器对象与我的UI绑定,如果教程是正确的,我不明白为什么它不应该工作,我有我的主窗口,当一个按钮被按下时,在tabPage内创建一个用户控制。这个用户控件包含了我的播放器。在主窗口中,我将把一个播放器传递给用户控件,这是我的主窗口。

 public partial class MainWindow : INotifyPropertyChanged
    { 
        public MainWindow()
        {
            DataContext = this;
            InitializeComponent();
        }

        private void AddPlayerClick(Object sender, RoutedEventArgs e)
        {
            AddTabItem("New Player", new AddPlayer(new Player(1, "asd", "asd", new DateTime(1,2,3), "asd", "asd", "asd", "asd", true)));
        }
        public void AddTabItem(String name, UserControl userControl)
        {
            TabItem tab = new TabItem
            {
                Header = name
            };
            userControl.DataContext = userControl;
            tab.Content = userControl;
            TabControl.Items.Add(tab);
        }

        public event PropertyChangedEventHandler PropertyChanged;
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] String propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

<Window x:Class="Tournament_App.MainWindow"
        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"
        xmlns:local="clr-namespace:Tournament_App"
        mc:Ignorable="d"
        Title="MainWindow" Height="1920" Width="1080">
    <StackPanel>
        <Menu>
            <MenuItem Header="Players">
                <MenuItem Header="Add Player" Click="AddPlayerClick"/>
            </MenuItem>
        </Menu>
    </StackPanel>
</Window>

当按钮被点击时,一个新的标签页被创建,一个播放器被传递过来。 然后在AddPlayer用户控件中,它被设置为一个带有公共getsetter的私有属性。

    public partial class AddPlayer : UserControl, INotifyPropertyChanged
    {
        private Player _player;
        public Player Player
        {
            get { return _player; }
            set { 
                     _player = value; 
                     OnPropertyChanged();
                 }
        }

        public AddPlayer(Player player)
        {
            DataContext = Player;
            InitializeComponent();

            Player = player;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] String propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

然后我将球员的名字绑定到一个文本框中。

<UserControl x:Class="Tournament_App.Views.AddPlayer"
             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:local="clr-namespace:Tournament_App.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" >
   <StackPanel>
        <Label Content="First Name" />
        <TextBox Text="{Binding FirstName, Mode=TwoWay}"/>
   </StackPanel>

据我所知,数据上下文是正确的。而且绑定的名字也都是正确的,所以我不明白为什么它不能工作。任何帮助将被感激。我相信我修改了太多的INotifyProperty,但我不太清楚我需要它们的位置。你可能可以告诉我,我对WPF很陌生。

c# wpf data-binding datacontext
1个回答
0
投票

首先,你设置的是 AddPlayer.DataContext 到未初始化的属性 Player 的构造函数内的 AddPlayer:

public AddPlayer(Player player)
{
  DataContext = Player; // Wrong! Property is not initialized.
  InitializeComponent(); // Wrong! InitializeComponent() should be the very first call

  Player = player;
}

这可能是一个错字。另外 InitializeComponent() 应该始终是第一个调用。

但在实例化了 AddPlayer 你覆盖 DataContext by setting it to theAddPlayer`本身。

public void AddTabItem(String name, UserControl userControl)
{
  TabItem tab = new TabItem
  {
    Header = name
  };
  userControl.DataContext = userControl; // Wrong! This overrides the constructor assignment. 
  tab.Content = userControl;
  TabControl.Items.Add(tab);
}

而不是将物品添加到 TabControl.ItemsSource 让控制权来处理 DataContext. 也千万不要实施 INotifyPropertyChanged 在控制上 DependencyObject. 始终执行 DependencyProperty 因为这有更好的表现。控件的属性也很有可能是以下几点 Binding.Target 并绑定到一个数据源。Binding.Target 必须DependencyProperty:

TabItemData.cs

public class TabItemData
{ 
  public TabItemData(string title, Player player)
  {
    this.Title = title;
    this.Player = player;
  }

  public Player Player { get; set; }
  public string Title { get; set; }
}

AddPlayer.xaml.cs

public partial class AddPlayer : UserControl, INotifyPropertyChanged
{
  public AddPlayer()
  {
    InitializeComponent();

    // Player is already the DataContext, set from XAML DataTemplate.
    // Access player like "var player = this.DataContext as Player;"
    // This instance is automatically created by a DataTemplate
  }
}

主窗口(MainWindow).xaml.cs.

public partial class MainWindow : Window
{ 
  public static readonly DependencyProperty PlayersProperty = DependencyProperty.Register(
  "Players",
  typeof(ObservableCollection<TabItemData>),
  typeof(MainWindow),
  new PropertyMetadata(default(ObservableCollection<TabItemData>)));

  public ObservableCollection<TabItemData> Players
  {
    get => (ObservableCollection<TabItemData>) GetValue(MainWindow.PlayersProperty);
    set => SetValue(MainWindow.PlayersProperty, value);
  }

  public MainWindow()
  {
    InitializeComponent();

    this.DataContext = this;
    this.Players = new ObservableCollection<TabItemData>();
  }

  private void AddPlayerClick(Object sender, RoutedEventArgs e)
  {
    this.Players.Add(new TabItemData("New Player", new Player(1, "asd", "asd", new DateTime(1,2,3), "asd", "asd", "asd", "asd", true)));
  }
}

MainWiindow.xaml.cs

<Window x:Name="Window">
  <StackPanel>
    <Menu>
      <MenuItem Header="Players">
        <MenuItem Header="Add Player" Click="AddPlayerClick" />
      </MenuItem>
    </Menu>

    <TabControl ItemsSource="{Binding ElementName=Window, Path=Players>
      <TabControl.ItemTemplate>
        <DataTemplate DataType="{x:Type TabItemData}">
          <TextBlock Text="{Binding Title}" />
        </DataTemplate>
      </TabControl.ItemTemplate>
      <TabControl.ContentTemplate>
        <DataTemplate DataType="{x:Type TabItemData}">
          <AddPlayer DataContext="{Binding Player}" />
        </DataTemplate>
      </TabControl.ContentTemplate>
    </TabControl>
  </StackPanel>
</Window>
© www.soinside.com 2019 - 2024. All rights reserved.