简体   繁体   English

WPF窗口未使用XAML中的绑定更新

[英]WPF window not updating with binding in XAML

Can someone please explain what's going on here? 有人可以解释一下这是怎么回事吗? I'm new to WPF and migrating my Forms project to WPF with binding. 我是WPF的新手,并通过绑定将Forms项目迁移到WPF。 I'm using AvalonDock but I'm not binding directly to any of the AvalonDock controls. 我正在使用AvalonDock,但没有直接绑定到任何AvalonDock控件。 Here's a couple excerpts. 这是几个摘录。 I removed a lot for brevity's sake but let me know if you need to see something else. 为了简洁起见,我删除了很多内容,但如果您需要查看其他内容,请告诉我。

EDIT: These two StackPanels are just tests... trying to figure this stuff out. 编辑:这两个StackPanels只是测试...试图弄清楚这些东西。
EDIT2 : I'm trying to do MVVM eventually; EDIT2 :我最终尝试做MVVM; I just need to get a better handle on binding so I know how to structure it. 我只需要对绑定有一个更好的了解,所以我知道如何构造它。
EDIT3 : See bottom of post. EDIT3 :请参阅文章底部。

Q: The first StackPanel does not update at all, never mind updating after changes. 问:第一个StackPanel根本不更新,更不用说在更改后更新。 I've tried setting the DataContext in the StackPanel , Grid and TextBlock . 我试过在StackPanelGridTextBlock设置DataContext What am I doing wrong? 我究竟做错了什么?

Q: The second works fine when the parent grid is bound in code behind but only if bound where you see it, not in the MainWindow_Loaded() method. 问:当父网格绑定在后面的代码中但仅在您看到它的位置绑定了,而不是在MainWindow_Loaded()方法中绑定时,第二种方法才能正常工作。 What's different here? 这里有什么不同?

I've read several tutorials as well as plenty of similar questions here but nothing's helping me understand what the difference is here and what I'm missing. 我在这里已经阅读了几本教程以及许多类似的问题,但是没有什么能帮助我理解这里的区别和所缺少的。

<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";
}

Entity and EntityViewModel implement INotifyPropertyChanged and it works just fine with the second StackPanel . EntityEntityViewModel实现INotifyPropertyChanged并且与第二个StackPanel The button that calls button_Click() is just for testing the binding. 调用button_Click()的按钮仅用于测试绑定。 EntityViewModel pretty much just wraps Entity through the ActiveEntity property and helps with reading the collections-of-collections within Entity . EntityViewModel几乎只是通过ActiveEntity属性来包装Entity ,并有助于读取Entity内的collection-of-collections。

EDIT3: EDIT3:

I've also tried a couple resources. 我也尝试了一些资源。 Here's how I did the ObjectDataProvider: 这是我做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}" />

It does work. 确实有效。 You might be probably updating a wrong viewmodel. 您可能正在更新错误的视图模型。

Once you define the viewmodel in DataContext , youd have to access it this way: 一旦在DataContext定义了视图模型,就必须以这种方式访问​​它:

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

Your first stack panel is not working, because the data context is inherited. 您的第一个堆栈面板不起作用,因为数据上下文是继承的。 Therefore, once you change the DataContext of the Grid to the ActiveEntity object, the binding on the text block in the first data context will set the datacontext for the TextBlock to the ActiveEntity on the current datacontext, which would already be the ActiveEntity (therefore ActiveEntity.ActiveEntity ) And than try to bind to the Label property on that. 因此,一旦将Grid的DataContext更改为ActiveEntity对象,在第一个数据上下文中的文本块上的绑定将把TextBlock的数据上下文设置为当前数据上下文上的ActiveEntity ,而该ActiveEntity已经是ActiveEntity (因此为ActiveEntity.ActiveEntity ),然后尝试将其绑定到Label属性。 Eg ActiveEntity.ActiveEntity.Label 例如ActiveEntity.ActiveEntity.Label

Before the click, you are setting the DataContext of that window to "this" which I am assuming is not the ViewModel, it is the code behind? 单击之前,您正在将该窗口的DataContext设置为“ this”,我假设这不是ViewModel,它是后面的代码吗?

If you are using MVVM, 如果您使用的是MVVM,

you should have something like this 你应该有这样的东西

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

Or some other ViewModel which provides all the necessary data. 或其他提供所有必要数据的ViewModel。

You nomrally would have a MainWindowView and MainWindowViewModel , at least that is the convention and usually you set the datacontext of the window in the constructor once(you can do it in the Loaded handler), in most cases you shouldn't need to manually change the DataContext of any framework elements in the code behind. 通常,您会拥有一个MainWindowViewMainWindowViewModel ,至少这是约定,通常您只需在构造函数中设置一次窗口的数据上下文(您可以在Loaded处理程序中进行设置),在大多数情况下,您无需手动更改后面代码中任何框架元素的DataContext。

EDIT: Example Code: 编辑:示例代码:

MainWindow.xaml 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 MainWindow.xaml.cs

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

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

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

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

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

As you can see, I am setting the DataContext of the Window to the MainViewModel, therefore the DataContext(root of all bindings) is the MainViewModel, and each TextBlock needs to first access the ActiveEntity property first, before it can get to the Label proeprty. 如您所见,我将Window的DataContext设置为MainViewModel,因此DataContext(所有绑定的根)是MainViewModel,每个TextBlock需要首先访问ActiveEntity属性,然后才能访问Label proeprty 。

THe other option is, that if everything inside the main stack panel you given us, will be bound to ActiveEntity, you can change the DataContext of that StackPanel, binding it to the ActiveEntity , and therefore all of its children datacontext will also be that object. 另一种选择是,如果你给我们的主堆叠面板里面的一切,将被绑定到ActiveEntity,你可以改变的DataContext是StackPanel中的,它结合了ActiveEntity ,因此,它的所有孩子的datacontext也将是该对象。

    <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>

EDIT 2 - Advice 编辑2-建议

You should refrain from referencing objects by name as much as possible, and have as little logic in the code behind as possible, if any. 您应该避免按名称引用对象,并且如果可能的话,在其背后的代码中尽可能少地包含逻辑。 For most simple screens there is no need to have anything in code behind other than the initial Binding of the DataContext (if you don't have a window service which creates + sets the DataContext of windows) 对于大多数简单的屏幕,除了DataContext的初始绑定之外,不需要在代码中包含任何其他内容(如果您没有创建+设置Windows的DataContext的窗口服务)

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

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