我一直在尝试将我的播放器对象与我的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很陌生。
首先,你设置的是 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 the
AddPlayer`本身。
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>