我正在努力解决这个问题这么多个小时:
我有名为NewMazeGrid
的网格的用户自定义控件,我想将其用作MainWindow
中的控件。 MainWindow
包含MazeViewModel
(mazeVM成员)。
当属性MazeViewModel:MySingleplay
发生变化时,我正在尝试设置网格的值。
(我正在使用INotifyPropertyChanged
,它完全正常。我猜,问题在于最终绑定)
代码:
这是属性MazeViewModel:MySingleplay
getter:
public string MySingleplay
{
get
{
if (myModel.MySingleplay == null)
{
return "";
} else
{
return myModel.MySingleplay.ToString();//works perfect
}
}
}
这是NewMazeGrid.xaml.cs
:
namespace VisualClient.View.controls
{
public partial class NewMazeGrid : UserControl
{
private MazePresentation myMaze;
private string order; //dont really use it
//Register Dependency Property
public static readonly DependencyProperty orderDependency =
DependencyProperty.Register("Order", typeof(string), typeof(NewMazeGrid));
public NewMazeGrid()
{
myMaze = new MazePresentation();
InitializeComponent();
DataContext = this;
lst.ItemsSource = myMaze.MazePuzzleLists;
}
public string Order
{
get
{
return (string)GetValue(orderDependency);
}
set
{
SetValue(orderDependency, value);
myMaze.setPresentation(value); //(parsing string into matrix)
}
}
}
}
这是MainWindow.xaml.cs
:
public partial class MainWindow : Window
{
private MazeViewModel mazeVM;
public MainWindow()
{
InitializeComponent();
mazeVM = new MazeViewModel(new ClientMazeModel(new TCPClientConnection()));
DataContext = mazeVM;
mazeVM.connectToServer();
}
private void bu_Click(object sender, RoutedEventArgs e)
{
bool isC = mazeVM.isConnected();
mazeVM.openSingleplayGame("NewMaze");//works perfect
}
这是MainWindow.xaml
:
<Window x:Class="VisualClient.View.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:Controls ="clr-namespace:VisualClient.View.controls"
xmlns:vm ="clr-namespace:VisualClient.ViewModel"
xmlns:local="clr-namespace:VisualClient.View"
mc:Ignorable="d"
Title="Main Window" Height="350" Width="525" MinWidth="900" MinHeight="600">
<WrapPanel >
<Button Name ="bu" Content="Click_Me" Click="bu_Click"/>
<Grid Name="myGrid">
<Controls:NewMazeGrid Order="{Binding MySingleplay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</WrapPanel>
</Window>
我在绑定行上收到此错误:值不能为空。
总结: 它初始化了ctor中的窗口,但是当属性发生变化时,它不会进入Order属性设置器。因此,我的网格永远不会改变。
在这种情况下,绑定的正确语法是什么?如何将其绑定到正确的属性?
答案 0 :(得分:0)
WPF可能不会调用依赖项属性的CLR包装器,而只是直接调用底层DependencyObject的GetValue
和SetValue
方法。这就是除了GetValue和SetValue调用之外不应该有任何逻辑的原因。
XAML Loading and Dependency Properties中解释了这一点:
因为当前的WPF实现了XAML处理器的行为 对于属性设置完全绕过包装器,你不应该 将任何其他逻辑放入包装器的set定义中 您的自定义依赖属性。如果你把这样的逻辑放在集合中 定义,然后在属性时不执行逻辑 在XAML而不是代码中设置。
类似地,XAML处理器的其他方面获得属性 来自XAML处理的值也使用GetValue而不是使用 包装。因此,您还应该避免任何额外的 getValue调用之外的get定义中的实现。
要获得有关属性值更改的通知,您可以按属性元数据注册PropertyChangedCallback
。另请注意,DependencyProperty
字段有一个命名约定。你应该被称为OrderProperty
:
public static readonly DependencyProperty OrderProperty =
DependencyProperty.Register(
"Order", typeof(string), typeof(NewMazeGrid),
new PropertyMetadata(OnOrderChanged));
public string Order
{
get { return (string)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
}
private static void OnOrderChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((NewMazeGrid)obj).myMaze.setPresentation((string)e.NewValue);
}
除此之外,你不能设置
DataContext = this;
NewMazeGrid
的构造函数中的。这有效地防止从父窗口继承DataContext,因此{Binding MySingleplay}
将不起作用。除非在特殊情况下,否则永远不会显式设置UserControl的DataContext。
因此,从构造函数中删除DataContext赋值:
public NewMazeGrid()
{
myMaze = new MazePresentation();
InitializeComponent();
lst.ItemsSource = myMaze.MazePuzzleLists;
}
也就是说,也没有必要在单向绑定上设置UpdateSourceTrigger=PropertyChanged
。它只对双向(或单向源)绑定有影响:
<Controls:NewMazeGrid Order="{Binding MySingleplay}"/>