WPF window 未更新 XAML 中的绑定
WPF window not updating with binding in XAML
有人可以解释一下这是怎么回事吗?我是 WPF 的新手,正在通过绑定将我的 Forms 项目迁移到 WPF。我正在使用 AvalonDock 但我没有直接绑定到任何 AvalonDock 控件。这里有几个摘录。为了简洁起见,我删除了很多内容,但如果您需要查看其他内容,请告诉我。
编辑: 这两个 StackPanel 只是测试...试图解决这些问题。
EDIT2:我最终尝试做 MVVM;我只是需要更好地处理绑定,所以我知道如何构建它。
EDIT3:参见 post 的底部。
问:第一个StackPanel
根本不更新,更不用说改了再更新。我试过在 StackPanel
、Grid
和 TextBlock
中设置 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";
}
Entity
和 EntityViewModel
实现了 INotifyPropertyChanged
,它与第二个 StackPanel
一起工作得很好。调用 button_Click()
的按钮仅用于测试绑定。 EntityViewModel
几乎只是通过 ActiveEntity
属性包装 Entity
并帮助读取 Entity
.
中的集合集合
编辑3:
我也尝试了一些资源。以下是我如何处理 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}" />
确实有效。您可能正在更新错误的视图模型。
在 DataContext
中定义视图模型后,您必须以这种方式访问它:
private void button_Click(object sender, RoutedEventArgs e)
{
var myModel = (ViewModel.EntityViewModel)(yourStackPanelName.DataContext);
myModel.ActiveEntity.Label = "test";
}
您的第一个堆栈面板不工作,因为数据上下文是继承的。因此,一旦您将 Grid 的 DataContext 更改为 ActiveEntity
对象,第一个数据上下文中文本块上的绑定会将 TextBlock 的数据上下文设置为当前数据上下文中的 ActiveEntity
,这已经是 ActiveEntity
(因此 ActiveEntity.ActiveEntity
),而不是尝试绑定到标签 属性 上。例如。 ActiveEntity.ActiveEntity.Label
点击之前,您将 window 的 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。
你通常会有 MainWindowView
和 MainWindowViewModel
,至少这是约定,通常你在构造函数中设置 window 的数据上下文一次(你可以做到在 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
proeprty 之前。
另一个选项是,如果您给我们的主堆栈面板内的所有内容都将绑定到 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 的初始绑定之外,不需要在代码中包含任何内容(如果您没有创建 + 设置 windows DataContext 的 window 服务)
有人可以解释一下这是怎么回事吗?我是 WPF 的新手,正在通过绑定将我的 Forms 项目迁移到 WPF。我正在使用 AvalonDock 但我没有直接绑定到任何 AvalonDock 控件。这里有几个摘录。为了简洁起见,我删除了很多内容,但如果您需要查看其他内容,请告诉我。
编辑: 这两个 StackPanel 只是测试...试图解决这些问题。
EDIT2:我最终尝试做 MVVM;我只是需要更好地处理绑定,所以我知道如何构建它。
EDIT3:参见 post 的底部。
问:第一个StackPanel
根本不更新,更不用说改了再更新。我试过在 StackPanel
、Grid
和 TextBlock
中设置 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";
}
Entity
和 EntityViewModel
实现了 INotifyPropertyChanged
,它与第二个 StackPanel
一起工作得很好。调用 button_Click()
的按钮仅用于测试绑定。 EntityViewModel
几乎只是通过 ActiveEntity
属性包装 Entity
并帮助读取 Entity
.
编辑3:
我也尝试了一些资源。以下是我如何处理 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}" />
确实有效。您可能正在更新错误的视图模型。
在 DataContext
中定义视图模型后,您必须以这种方式访问它:
private void button_Click(object sender, RoutedEventArgs e)
{
var myModel = (ViewModel.EntityViewModel)(yourStackPanelName.DataContext);
myModel.ActiveEntity.Label = "test";
}
您的第一个堆栈面板不工作,因为数据上下文是继承的。因此,一旦您将 Grid 的 DataContext 更改为 ActiveEntity
对象,第一个数据上下文中文本块上的绑定会将 TextBlock 的数据上下文设置为当前数据上下文中的 ActiveEntity
,这已经是 ActiveEntity
(因此 ActiveEntity.ActiveEntity
),而不是尝试绑定到标签 属性 上。例如。 ActiveEntity.ActiveEntity.Label
点击之前,您将 window 的 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。
你通常会有 MainWindowView
和 MainWindowViewModel
,至少这是约定,通常你在构造函数中设置 window 的数据上下文一次(你可以做到在 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
proeprty 之前。
另一个选项是,如果您给我们的主堆栈面板内的所有内容都将绑定到 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 的初始绑定之外,不需要在代码中包含任何内容(如果您没有创建 + 设置 windows DataContext 的 window 服务)