C# 将堆栈(可以切换)绑定到列表框

C# 将堆栈(可以切换)绑定到列表框,c#,wpf,data-binding,C#,Wpf,Data Binding,我有一个对象列表(Man),每个对象都包含一个状态堆栈。 我有一个调试窗口,在列表框中显示选定的人的堆栈。 我有一个TabControl,我用它来选择一个人进行调试 为了能够选择正确的绑定,我创建了一个属性,它返回选项卡控件的所选索引处的人的状态堆栈 public object StateStack => Men[DebugIndex].States; DebugIndex绑定到TabControl的SelectedIndex属性。因此,为了使DebugIndex更新StateStack

我有一个对象列表(
Man
),每个对象都包含一个状态堆栈。 我有一个调试窗口,在
列表框中显示选定的
人的堆栈。
我有一个TabControl,我用它来选择一个人进行调试

为了能够选择正确的绑定,我创建了一个属性,它返回
选项卡控件
的所选索引处的人的
状态堆栈

public object StateStack => Men[DebugIndex].States;
DebugIndex
绑定到
TabControl
SelectedIndex
属性。因此,为了使
DebugIndex
更新
StateStack
以显示,我使用了
OnPropertyChanged

public int DebugIndex {
    get => _debugIndex;
    set {
        _debugIndex = value;
        OnPropertyChanged(nameof(StateStack));
    }
}
问题是,当
选项卡控件
SelectedIndex
发生变化时,
堆栈
异常无序!问题是它只是在视图中无序,而不是在数据中

我想这是因为我改变了绑定的引用,这是另一个堆栈,但我不知道如何解决这个问题

顺便说一句,当我添加所有Man对象并在开始时初始化它们的StateStack时,它就起作用了。但当我稍后添加一个人(并初始化其状态堆栈)时,例如当我单击一个按钮时,它就不再工作了

public sealed partial class MainWindow : INotifyPropertyChanged {

    private int _debugIndex;

    public ObservableCollection<Man> Men { get; } = new ObservableCollection<Man>();

    public MainWindow() {
        Men.Add(new Man {Index = 0, States = new StateStack()});

        InitializeComponent();

        Men[0].States.Push(new State {Name = "Falling1"});
        Men[0].States.Push(new State {Name = "Walking1"});

        //this is simplified code. I push states here because in my program it's done during runtime (not during initialization)
    }

    public object StateStack => Men[DebugIndex].States;

    public int DebugIndex {
        get => _debugIndex;
        set {
            _debugIndex = value;
            OnPropertyChanged(nameof(StateStack));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string propertyName = null) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e) {
        Men.Add(new Man {Index = 1, States = new StateStack()});

        Men[1].States.Push(new State {Name = "Falling2"});
        Men[1].States.Push(new State {Name = "Walking2"});
        Men[1].States.Push(new State {Name = "Running2"});
    }
}

public class Man {
    public int Index { get; set; }
    public StateStack States { get; set; }
}

public class State {
    public string Name { private get; set; }

    public override string ToString() {
        return Name;
    }
}

public sealed class StateStack : Stack<State>, INotifyCollectionChanged {

    public new void Push(State item) {
        base.Push(item);

        OnCollectionChanged(
            new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, Count - 1));
    }

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    private void OnCollectionChanged(NotifyCollectionChangedEventArgs e) {
        CollectionChanged?.Invoke(this, e);
    }
}
公共密封部分类主窗口:INotifyPropertyChanged{
私有int_索引;
public observetecollection Men{get;}=new observetecollection();
公共主窗口(){
Add(newman{Index=0,States=newstatestack()});
初始化组件();
Men[0].States.Push(新状态{Name=“Falling1”});
Men[0].States.Push(新状态{Name=“Walking1”});
//这是简化的代码。我在这里推送状态,因为在我的程序中,它是在运行时完成的(不是在初始化期间)
}
公共对象StateStack=>Men[DebugIndex]。状态;
公共整数索引{
get=>\u debugIndex;
设置{
_指数=数值;
OnPropertyChanged(名称(StateStack));
}
}
公共事件属性更改事件处理程序属性更改;
私有void OnPropertyChanged([CallerMemberName]字符串propertyName=null){
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
private void按钮base_OnClick(对象发送方,RoutedEventTarget e){
Add(newman{Index=1,States=newstatestack()});
Men[1].States.Push(新状态{Name=“Falling2”});
Men[1].States.Push(新状态{Name=“Walking2”});
Men[1].States.Push(新状态{Name=“Running2”});
}
}
公务舱员{
公共int索引{get;set;}
公共状态堆栈状态{get;set;}
}
公共阶级国家{
公共字符串名称{private get;set;}
公共重写字符串ToString(){
返回名称;
}
}
公共密封类StateStack:堆栈,INotifyCollectionChanged{
公共新作废推送(状态项){
基础.推(项);
OnCollectionChanged(
新的NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,item,Count-1);
}
公共事件通知CollectionChangedEventHandler CollectionChanged;
CollectionChanged上的私有void(NotifyCollectionChangedEventArgs e){
CollectionChanged?调用(this,e);
}
}
和我的视图代码:

<Window x:Class="ObservableStackBug.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"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" DataContext="{Binding RelativeSource={RelativeSource Self}}">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Button Content="Add" Margin="5" Padding="8 2" HorizontalAlignment="Left" Click="ButtonBase_OnClick"/>

        <ListBox ItemsSource="{Binding StateStack}" Grid.Row="1" />

        <TabControl Grid.Row="2" ItemsSource="{Binding Men}" SelectedIndex="{Binding DebugIndex}">
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Index}" />
                </DataTemplate>
            </TabControl.ItemTemplate>
        </TabControl>
    </Grid>

</Window>


对于我的绑定,当DebugIndex更改时,StateStack是另一个堆栈,我能做些什么呢?

我模拟了您的场景,观察到
Push
方法在如何将
NotifyCollectionChangedEventArgs
项更改传播到源代码方面存在问题。当前代码通知从结束索引更改项目(但对于堆栈,项目添加在顶部)。如果将通知开始索引更新为
0
作为
NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,item,0)
,则绑定源将在视图中以适当的顺序显示该项。您可以阅读关于NotifyCollectionChangedEventArgs的信息


也为
StateStack
属性添加
OnPropertyChanged
,因此当
StateStack
的绑定更改时,它也会通知绑定的源。此外,您将
StateStack
定义为对象的任何特定原因。(您提到的问题并不是特定于对象返回类型的-它更像是一个编码观察)@user1672994我将更改它,这只是因为我使用了ReSharper^^@user1672994的自动属性创建,您所说的不起作用,并且我已经使用了OnPropertyChanged(nameof(StateStack));在
DebugIndex
属性中,我模拟了您的场景,观察到推送方法存在问题,无法将
NotifyCollectionChangedEventArgs
项更改传播到源。当前代码通知从结束索引中添加项。如果将通知更新为NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,item,0),则绑定源将在视图中以适当的顺序显示该项目。谢谢,这太好了。为了给将来可能的读者提供信息,我也使用了0作为Pop()方法。。。
public new void Push(State item) {
    base.Push(item);

    OnCollectionChanged(
        new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, 0));
}