【问题标题】:WPF window not updating with binding in XAMLWPF 窗口未使用 XAML 中的绑定进行更新
【发布时间】:2015-01-03 22:46:59
【问题描述】:

有人可以解释一下这里发生了什么吗?我是 WPF 的新手,我正在通过绑定将我的 Forms 项目迁移到 WPF。我正在使用 AvalonDock,但我没有直接绑定到任何 AvalonDock 控件。这里有几个摘录。为简洁起见,我删除了很多内容,但如果您需要查看其他内容,请告诉我。

编辑:这两个 StackPanel 只是测试......试图弄清楚这些东西。
EDIT2:我最终会尝试做 MVVM;我只需要更好地处理绑定,以便知道如何构建它。
EDIT3:见帖子底部。

Q:第一个StackPanel根本不更新,更别提更新了。我尝试在StackPanelGridTextBlock 中设置DataContext。我做错了什么?

问:当父网格绑定在后面的代码中时,第二个工作正常,但前提是绑定在您看到它的位置,而不是在MainWindow_Loaded() 方法中。这里有什么不同?

我在这里阅读了几个教程以及许多类似的问题,但没有什么能帮助我理解这里有什么区别以及我缺少什么。

<Window x:Class="TestUIWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ad="http://schemas.xceed.com/wpf/xaml/avalondock"
        Title="MainWindow" Height="768" Width="1024"
        Loaded="MainWindow_Loaded"
        xmlns:vm="clr-namespace:TestUIWPF.ViewModel"
        >
<!-- lots excluded for brevity. there are no Window.Resources -->
<ad:LayoutAnchorable Title="Test" >
    <Grid x:Name="gridTest">
        <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
                <StackPanel.DataContext>
                    <vm:EntityViewModel />
                </StackPanel.DataContext>
                <TextBlock Text="Label" />
                <TextBlock DataContext="{Binding ActiveEntity}" Text="{Binding Path=Label}" />
            </StackPanel>

            <StackPanel Orientation="Horizontal" >
                <TextBlock Text="Label Again" />
                <TextBlock Text="{Binding Path=Label}" />
            </StackPanel>
        </StackPanel>
    </Grid>
</ad:LayoutAnchorable>
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    this.DataContext = this;
    SelectedEntityViewModel = new ViewModel.EntityViewModel();
    ImportEntityXML_Click(null, null); //skips clicking the menus
}
private void ImportEntityXML_Click(object sender, RoutedEventArgs e)
{
    //omitted OpenFileDialog and XmlReader stuff
    xmlreader = new XmlReader(dlg.FileName);
    Entities.Add(xmlreader.ReadEntityFromXML());
    SimulatedEntitySelection(Entities.ElementAt(0)); //haven't built any of the UI stuff for this yet
}

private void SimulatedEntitySelection(Entity ent)
{
    SelectedEntityViewModel.ActiveEntity = ent;
    gridTest.DataContext = SelectedEntityViewModel.ActiveEntity;
}

private void button_Click(object sender, RoutedEventArgs e) 
{
    SelectedEntityViewModel.ActiveEntity.Label = "test";
}

EntityEntityViewModel 实现 INotifyPropertyChanged 并且它与第二个 StackPanel 一起工作得很好。调用button_Click() 的按钮仅用于测试绑定。 EntityViewModel 几乎只是通过 ActiveEntity 属性包装 Entity 并帮助读取 Entity 中的集合。

EDIT3:

我还尝试了一些资源。以下是我执行 ObjectDataProvider 的方法:

<Window.Resources>
    <ObjectDataProvider x:Key="testVM" ObjectType="{x:Type vm:EntityViewModel}" />
    <vm:EntityViewModel x:Key="SelEntVM" />
</Window.Resources>
<!-- .... -->
<StackPanel.DataContext>
    <Binding Source="{StaticResource testVM}" />
</StackPanel.DataContext>
<TextBlock Text="Label" />
<TextBlock Text="{Binding Path=ActiveEntity.Label}" />

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    您的第一个堆栈面板不起作用,因为数据上下文是继承的。因此,一旦将 Grid 的 DataContext 更改为 ActiveEntity 对象,第一个数据上下文中文本块上的绑定会将 TextBlock 的 datacontext 设置为当前 datacontext 上的 ActiveEntity,这已经是ActiveEntity(因此 ActiveEntity.ActiveEntity)然后尝试绑定到 Label 属性。例如。 ActiveEntity.ActiveEntity.Label

    在单击之前,您将该窗口的 DataContext 设置为“this”,我假设这不是 ViewModel,它是背后的代码?

    如果你使用的是 MVVM,

    你应该有这样的东西

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        SelectedEntityViewModel = new ViewModel.EntityViewModel();
        this.DataContext = SelectedEntityViewModel;
        ImportEntityXML_Click(null, null); //skips clicking the menus
    }
    

    或其他提供所有必要数据的 ViewModel。

    您通常会有MainWindowViewMainWindowViewModel,至少这是约定,通常您在构造函数中设置一次窗口的数据上下文(您可以在Loaded 处理程序中进行),在大多数情况下在这种情况下,您不需要在后面的代码中手动更改任何框架元素的 DataContext。

    编辑:示例代码:

    MainWindow.xaml

    <Window x:Class="SO27760357.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="300" Width="300">
        <Grid>
            <StackPanel Orientation="Vertical">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Label" />
                    <TextBlock Text="{Binding ActiveEntity.Label}"/>
                </StackPanel>
    
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Label Again" />
                    <TextBlock Text="{Binding ActiveEntity.Label}" />
                </StackPanel>
            </StackPanel>
        </Grid>
    </Window>
    

    MainWindow.xaml.cs

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }
    }
    

    MainWindowViewModel.cs(为简单起见,省略了 INotifyPropertChanged)

    public class MainWindowViewModel
    {
        public EntityViewModel ActiveEntity { get; set; }
    }
    

    EntityViewModel.cs(为简单起见,省略了 INotifyPropertChanged)

    public class EntityViewModel
    {
        public string Label { get; set; }
    }
    

    如您所见,我将Window的DataContext设置为MainViewModel,因此DataContext(所有绑定的根)是MainViewModel,每个TextBlock需要先访问ActiveEntity属性,然后才能前往Label 物业。

    另一个选项是,如果您提供给我们的主堆栈面板内的所有内容都将绑定到 ActiveEntity,您可以更改该 StackPanel 的 DataContext,将其绑定到 ActiveEntity,因此它的所有子数据上下文也将是那个对象。

        <StackPanel Orientation="Vertical"
                    **DataContext="{Binding ActiveEntity}"**>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Label" />
                <TextBlock **Text="{Binding Label}"**/>
            </StackPanel>
    
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Label Again" />
                <TextBlock **Text="{Binding Label}"** />
            </StackPanel>
        </StackPanel>
    

    编辑 2 - 建议

    您应该尽可能避免按名称引用对象,并且尽可能少地在代码中包含逻辑(如果有的话)。对于大多数简单的屏幕,除了 DataContext 的初始绑定(如果您没有创建 + 设置窗口的 DataContext 的窗口服务)之外,不需要在代码中添加任何内容

    【讨论】:

    • 天哪。你直接击中了它的头。谢谢! Chris Eelmaa 确实指出了我对第一个 StackPanel 的问题,但您确实为我澄清了 DataContext 事物的继承。
    • 很高兴我能帮上忙 :-) 祝你好运,MVVM,2 个快速说明,你应该有一个 MainViewModel/MainWindowViewModel 类,并且所有的点击都应该尽可能由命令处理。将使代码更整洁,更易于维护。 codeproject.com/Articles/274982/Commands-in-MVVM#example1
    • 如果可以的话,我会再次支持你。我知道我的方法有点肮脏,但我只是为了解决问题而采用它。 Chris 的回答解决了我对这个“脏”代码的问题,但是为我的整个 MainWindow 设置 DataContext (它取代了我最终将构建的视图)确保我可以将所有这些代码移动到真正的视图中它...在学习 WPF、XAML 和绑定时,我一直在努力平衡“让它工作”和“为未来做计划”。再次感谢!
    【解决方案2】:

    确实有效。您可能更新了错误的视图模型。

    DataContext 中定义视图模型后,您必须以这种方式访问​​它:

    private void button_Click(object sender, RoutedEventArgs e) 
    {
        var myModel = (ViewModel.EntityViewModel)(yourStackPanelName.DataContext);
        myModel.ActiveEntity.Label = "test";
    }
    

    【讨论】:

    • 哦,伙计......在试图让它工作的所有愤怒阴霾中,我确实忘记为第一个 StackPanel 设置这个。我将不得不接受Michal Ciechan's answer,尽管他关于 DataContext 继承的注释确实为我澄清了整个问题。
    猜你喜欢
    • 2013-08-06
    • 2016-03-23
    • 1970-01-01
    • 2021-08-06
    • 2012-12-09
    • 1970-01-01
    • 1970-01-01
    • 2021-01-24
    相关资源
    最近更新 更多