【问题标题】:wpf c# data binding to set string using property of viewModel objectwpf c#数据绑定使用viewModel对象的属性设置字符串
【发布时间】:2026-02-05 23:25:01
【问题描述】:

我花了好几个小时试图解决这个问题:

我有一个名为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 属性设置器。因此我的网格永远不会改变。

在这种情况下,正确的绑定语法应该是什么?如何将其绑定到正确的属性?

Folders hierarchy explorer

【问题讨论】:

  • 不确定绑定错误,但不知道为什么不调用你的 setter 看看here
  • 谢谢!但仍然没有进入 MySingleplay 属性 getter

标签: c# wpf xaml mvvm data-binding


【解决方案1】:

WPF 可能不会调用依赖属性的 CLR 包装器,而只是直接调用底层 DependencyObject 的 GetValueSetValue 方法。这就是为什么除了 GetValue 和 SetValue 调用之外不应该有任何逻辑。

这在XAML Loading and Dependency Properties中有解释:

因为 XAML 处理器行为的当前 WPF 实现 对于属性设置完全绕过包装,你不应该 将任何附加逻辑放入包装器的集合定义中 您的自定义依赖项属性。如果你把这样的逻辑放在集合中 定义,那么当属性为 在 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}"/>

【讨论】:

  • 感谢您的评论!我添加了 OnOrderChanged 函数,但是当它通知更改时,它仍然没有进入 MySingleplay getter
  • 谢谢!现在它进入属性并更改 myGrid 的列表。但更改仍然没有出现在屏幕上。我想知道删除DataContext = this 是否有问题?我使用此代码将列表显示到标签的网格:link
  • 如果 NewMazeGrid 的 XAML 中存在“内部”绑定(即到它自己的属性),那么您可能必须在这些绑定上设置 RelativeSource={RelativeSource AncestorType=UserControl}
  • 我从 Lists+propertyChanged 更改为 ObservableCollection,现在它可以工作了!非常感谢! :D