wpf c#data binding使用viewModel对象的属性设置字符串

时间:2016-05-06 00:58:06

标签: c# wpf xaml mvvm data-binding

我正在努力解决这个问题这么多个小时:

我有名为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

1 个答案:

答案 0 :(得分:0)

WPF可能不会调用依赖项属性的CLR包装器,而只是直接调用底层DependencyObject的GetValueSetValue方法。这就是除了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}"/>